1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23
24 #ifdef ENABLE_EOB
25
26 #include "common/system.h"
27 #include "kyra/resource/resource.h"
28 #include "kyra/graphics/screen_eob.h"
29 #include "kyra/graphics/screen_eob_segacd.h"
30
31 namespace Kyra {
32
sega_initGraphics()33 void Screen_EoB::sega_initGraphics() {
34 _segaRenderer = new SegaRenderer(this);
35 _segaRenderer->setResolution(320, 224);
36 _segaRenderer->setPlaneTableLocation(SegaRenderer::kPlaneA, 0xC000);
37 _segaRenderer->setPlaneTableLocation(SegaRenderer::kPlaneB, 0xE000);
38 _segaRenderer->setPlaneTableLocation(SegaRenderer::kWindowPlane, 0xF000);
39 _segaRenderer->setupPlaneAB(512, 512);
40 _segaRenderer->setupWindowPlane(0, 0, SegaRenderer::kWinToLeft, SegaRenderer::kWinToTop);
41 _segaRenderer->setHScrollTableLocation(0xD800);
42 _segaRenderer->setSpriteTableLocation(0xDC00);
43 _segaAnimator = new SegaAnimator(_segaRenderer);
44 }
45
sega_selectPalette(int srcPalID,int dstPalID,bool set)46 void Screen_EoB::sega_selectPalette(int srcPalID, int dstPalID, bool set) {
47 if (srcPalID < -1 || srcPalID > 59 || dstPalID < 0 || dstPalID > 3)
48 return;
49
50 const uint16 *src = &_segaCurPalette[dstPalID << 4];
51 uint8 rgbColors[48];
52 uint8 *dst = rgbColors;
53
54 if (srcPalID >= 31 && srcPalID <= 38) {
55 src = &_segaCustomPalettes[(srcPalID - 31) << 4];
56 } else if (srcPalID >= 0) {
57 int temp = 0;
58 const uint16 *palettes = _vm->staticres()->loadRawDataBe16(kEoB1PalettesSega, temp);
59 if (!palettes)
60 return;
61 src = &palettes[srcPalID << 4];
62 }
63
64 // R: bits 1, 2, 3 G: bits 5, 6, 7 B: bits 9, 10, 11
65 for (int i = 0; i < 16; ++i) {
66 uint16 in = *src++;
67 _segaCurPalette[dstPalID << 4 | i] = in;
68 #if 0
69 static const uint8 col[8] = { 0, 52, 87, 116, 144, 172, 206, 255 };
70 *dst++ = col[CLIP<int>(((in & 0x00F) >> 1) + _palFaders[dstPalID]._brCur, 0, 7)];
71 *dst++ = col[CLIP<int>(((in & 0x0F0) >> 5) + _palFaders[dstPalID]._brCur, 0, 7)];
72 *dst++ = col[CLIP<int>(((in & 0xF00) >> 9) + _palFaders[dstPalID]._brCur, 0, 7)];
73 #else
74 *dst++ = CLIP<int>(((in & 0x00F) >> 1) + _palFaders[dstPalID]._brCur, 0, 7) * 255 / 7;
75 *dst++ = CLIP<int>(((in & 0x0F0) >> 5) + _palFaders[dstPalID]._brCur, 0, 7) * 255 / 7;
76 *dst++ = CLIP<int>(((in & 0xF00) >> 9) + _palFaders[dstPalID]._brCur, 0, 7) * 255 / 7;
77 #endif
78 }
79
80 getPalette(0).copy(rgbColors, 0, 16, dstPalID << 4);
81
82 if (_specialColorReplace) {
83 const uint8 swapColors[6] = { 0x08, 0x09, 0x0C, 0x0D, 0x0E, 0x0F };
84 for (int i = 0; i < 6; ++i)
85 getPalette(0).copy(getPalette(0), 0x10 | swapColors[i], 1, swapColors[i]);
86 }
87
88 if (set)
89 setScreenPalette(getPalette(0));
90 }
91
sega_loadCustomPaletteData(Common::ReadStream * in)92 void Screen_EoB::sega_loadCustomPaletteData(Common::ReadStream *in) {
93 uint16 *dst = _segaCustomPalettes;
94 for (int i = 0; i < 8; ++i) {
95 *dst++ = 0;
96 in->readUint16BE();
97 if (in->eos())
98 break;
99 for (int ii = 1; ii < 16; ++ii)
100 *dst++ = in->readUint16BE();
101 }
102 }
103
sega_updatePaletteFaders(int palID)104 void Screen_EoB::sega_updatePaletteFaders(int palID) {
105 int first = 0;
106 int last = 3;
107
108 if (palID >= 0)
109 first = last = palID;
110
111 bool update = false;
112 for (int i = first; i <= last; ++i) {
113 PaletteFader &f = _palFaders[i];
114 f._needRefresh = false;
115 if (f._fadeDelay == 0 && f._brCur != f._brDest) {
116 f._brCur = f._brDest;
117 f._needRefresh = true;
118 }
119 if (f._brCur == f._brDest)
120 continue;
121 if (--f._fadeTimer)
122 continue;
123
124 f._brCur += f._fadeIncr;
125 f._fadeTimer = f._fadeDelay;
126 f._needRefresh = true;
127 }
128
129 for (int i = first; i <= last; ++i) {
130 if (_palFaders[i]._needRefresh) {
131 sega_selectPalette(-1, i, true);
132 update = true;
133 _palFaders[i]._needRefresh = false;
134 }
135 }
136
137 if (update)
138 updateScreen();
139 }
140
sega_fadePalette(int delay,int16 brEnd,int dstPalID,bool waitForCompletion,bool noUpdate)141 void Screen_EoB::sega_fadePalette(int delay, int16 brEnd, int dstPalID, bool waitForCompletion, bool noUpdate) {
142 int first = 0;
143 int last = 3;
144 uint32 tickMillis = 0;
145
146 if (dstPalID >= 0)
147 first = last = dstPalID;
148
149 if (!noUpdate) {
150 for (int i = first; i <= last; ++i) {
151 PaletteFader &f = _palFaders[i];
152 f._needRefresh = false;
153 if (f._brCur < brEnd)
154 f._fadeIncr = 1;
155 else if (f._brCur > brEnd)
156 f._fadeIncr = -1;
157 else
158 continue;
159
160 f._brDest = brEnd;
161 f._fadeDelay = f._fadeTimer = delay;
162 }
163 }
164
165 if (!waitForCompletion)
166 return;
167
168 for (bool runLoop = true; runLoop; ) {
169 uint32 now = _vm->_system->getMillis();
170 sega_updatePaletteFaders(dstPalID);
171
172 runLoop = false;
173 for (int i = first; i <= last; ++i) {
174 if (_palFaders[i]._brCur != _palFaders[i]._brDest)
175 runLoop = true;
176 }
177
178 tickMillis += 16667;
179 uint32 ms = tickMillis / 1000;
180 _vm->delayUntil(now + ms);
181 tickMillis -= (ms * 1000);
182
183 if (_vm->shouldQuit()) {
184 for (int i = first; i <= last; ++i)
185 _palFaders[i]._fadeDelay = 0;
186 }
187 }
188 }
189
sega_paletteOps(int16 op,int16 par1,int16 par2)190 void Screen_EoB::sega_paletteOps(int16 op, int16 par1, int16 par2) {
191 assert(op >= 0 && op <= 6);
192 switch (op) {
193 case 6:
194 // Force palette update and wait for completion
195 break;
196 case 5:
197 // Force palette update, don't wait
198 break;
199 case 4:
200 _specialColorReplace = par1;
201 break;
202 default:
203 sega_fadePalette(par2, par1, op, false);
204 }
205 }
206
sega_setTextBuffer(uint8 * buffer,uint32 bufferSize)207 void Screen_EoB::sega_setTextBuffer(uint8 *buffer, uint32 bufferSize) {
208 if (!buffer) {
209 _textRenderBuffer = _defaultRenderBuffer;
210 _textRenderBufferSize = _defaultRenderBufferSize;
211 } else {
212 _textRenderBuffer = buffer;
213 _textRenderBufferSize = bufferSize;
214 }
215 }
216
sega_clearTextBuffer(uint8 col)217 void Screen_EoB::sega_clearTextBuffer(uint8 col) {
218 memset(_textRenderBuffer, col, _textRenderBufferSize);
219 }
220
sega_loadTextBackground(const uint8 * src,uint16 size)221 void Screen_EoB::sega_loadTextBackground(const uint8 *src, uint16 size) {
222 assert(size <= _textRenderBufferSize);
223 memcpy(_textRenderBuffer, src, size);
224 }
225
sega_drawTextBox(int pW,int pH,int x,int y,int w,int h,uint8 color1,uint8 color2)226 void Screen_EoB::sega_drawTextBox(int pW, int pH, int x, int y, int w, int h, uint8 color1, uint8 color2) {
227 sega_drawClippedLine(26, 5, x, y, w, 1, color1);
228 sega_drawClippedLine(26, 5, x, y + h - 1, w, 1, color1);
229 sega_drawClippedLine(26, 5, x, y, 1, h, color1);
230 sega_drawClippedLine(26, 5, x + w - 1, y, 1, h, color1);
231 sega_drawClippedLine(26, 5, x + 1, y + 1, w - 2, 1, color2);
232 sega_drawClippedLine(26, 5, x + 1, y + h - 2, w - 2, 1, color2);
233 sega_drawClippedLine(26, 5, x + 1, y + 1, 1, h - 2, color2);
234 sega_drawClippedLine(26, 5, x + w - 2, y + 1, 1, h - 2, color2);
235 }
236
sega_loadTextBufferToVRAM(uint16 srcOffset,uint16 addr,int size)237 void Screen_EoB::sega_loadTextBufferToVRAM(uint16 srcOffset, uint16 addr, int size) {
238 _segaRenderer->loadToVRAM(_textRenderBuffer + srcOffset, size, addr);
239 }
240
sega_gfxScale(uint8 * out,uint16 w,uint16 h,uint16 pitch,const uint8 * in,const uint16 * stampMap,const uint16 * traceVectors)241 void Screen_EoB::sega_gfxScale(uint8 *out, uint16 w, uint16 h, uint16 pitch, const uint8 *in, const uint16 *stampMap, const uint16 *traceVectors) {
242 // Implement only the required functions. No support for stamp size other than 0 or for rotation/flipping.
243 while (h--) {
244 uint32 xt = *traceVectors++ << 8;
245 uint32 yt = *traceVectors++ << 8;
246 int16 hStep = (int16)(*traceVectors++);
247 int16 vStep = (int16)(*traceVectors++);
248 uint8 hcnt = 0;
249 uint8 *out2 = out;
250
251 for (int x = 0; x < w; ++x) {
252 uint16 s = stampMap[((yt >> 11) & 0xF0) + ((xt >> 15) & 0x0F)];
253 uint8 val = 0;
254 //uint16 rotateFlip = (s >> 11) & 0x1C;
255 s &= 0x7FF;
256 if (!(yt & 0xF80000) && !(xt & 0xF80000) && s) {
257 val = in[(s << 7) + ((yt >> 9) & 0x3C) + ((xt >> 8) & 0x40) + ((xt >> 12) & 3)];
258 if (!(xt & 0x800))
259 val >>= 4;
260 }
261
262 if (x & 1)
263 *out++ |= (val & 0x0F);
264 else
265 *out = val << 4;
266
267 xt += hStep;
268 yt += vStep;
269 if (++hcnt == 8) {
270 out = out + (pitch << 5) + 28;
271 hcnt = 0;
272 }
273 }
274 out = out2 + 4;
275 }
276 }
277
sega_drawClippedLine(int pW,int pH,int x,int y,int w,int h,uint8 color)278 void Screen_EoB::sega_drawClippedLine(int pW, int pH, int x, int y, int w, int h, uint8 color) {
279 uint8 *dst = _textRenderBuffer;
280 uint8 p = (x & 1) ? 0x0F : 0xF0;
281 color &= p;
282 p = ~p;
283
284 dst += ((((y >> 3) * pW + (x >> 3)) << 5) + ((y & 7) << 2) + ((x & 7) >> 1));
285
286 while (h--) {
287 uint8 *dst2 = dst;
288 uint8 p2 = p;
289 uint8 col2 = color;
290 for (int i = x; i < x + w; ++i) {
291 *dst = (*dst & p) | color;
292 p = ~p;
293 color = (color << 4) | (color >> 4);
294 if (i & 1)
295 dst++;
296 if ((i & 7) == 7)
297 dst += 28;
298 }
299 dst = dst2 + 4;
300 color = col2;
301 p = p2;
302 if ((++y & 7) == 0)
303 dst = dst + (pW << 5) - 32;
304 }
305 }
306
sega_convertShape(const uint8 * src,int w,int h,int pal,int hOffs)307 uint8 *Screen_EoB::sega_convertShape(const uint8 *src, int w, int h, int pal, int hOffs) {
308 uint8 *shp = new uint8[(w >> 1) * h + 20];
309 uint8 *dst = shp;
310 *dst++ = 2;
311 *dst++ = h;
312 *dst++ = w >> 3;
313 *dst++ = h + hOffs;
314 *dst++ = 0;
315
316 for (int i = 1; i < 16; i++)
317 *dst++ = (pal << 4) | i;
318
319 const uint8 *pos = src;
320 for (int i = 0; i < h; ++i) {
321 const uint8 *pos2 = pos;
322 for (int ii = 0; ii < (w >> 1); ++ii) {
323 *dst++ = *pos;
324 pos += h;
325 }
326 pos = pos2 + 1;
327 }
328
329 return shp;
330 }
331
sega_encodeShapesFromSprites(const uint8 ** dst,const uint8 * src,int numShapes,int w,int h,int pal,bool removeSprites)332 void Screen_EoB::sega_encodeShapesFromSprites(const uint8 **dst, const uint8 *src, int numShapes, int w, int h, int pal, bool removeSprites) {
333 int spriteSize = (w * h) >> 1;
334 _segaRenderer->loadToVRAM(src, numShapes * spriteSize, 0);
335 int hw = (((w >> 3) - 1) << 2) | ((h >> 3) - 1);
336
337 int cp = setCurPage(Screen_EoB::kSegaInitShapesPage);
338
339 for (int l = 0, s = 0; s < numShapes; l = s) {
340 for (int i = s; i < numShapes; ++i) {
341 _segaAnimator->initSprite(s % 80, ((s % 80) * w) % SCREEN_W, ((s % 80) / (SCREEN_W / w)) * h, ((pal << 13) | (i * (w >> 3) * (h >> 3))), hw);
342 if (((++s) % 80) == 0)
343 break;
344 }
345
346 _segaAnimator->update();
347 _segaRenderer->render(Screen_EoB::kSegaInitShapesPage, -1, -1, -1, -1, true);
348
349 for (int i = l; i < s; ++i)
350 dst[i] = encodeShape((((i % 80) * w) % SCREEN_W) >> 3, ((i % 80) / (SCREEN_W / w)) * h, w >> 3, h);
351
352 clearPage(Screen_EoB::kSegaInitShapesPage);
353 }
354
355 if (removeSprites) {
356 _segaAnimator->clearSprites();
357 _segaAnimator->update();
358 _segaRenderer->memsetVRAM(0, 0, numShapes * spriteSize);
359 }
360
361 setCurPage(cp);
362 }
363
364 #if SEGA_PERFORMANCE
365 #define mRenderLineFragment(hFlip, oddStart, oddEnd, useMask, dst, mask, src, start, end, pal) \
366 { \
367 int rlfOffs = 0; \
368 if (hFlip) \
369 rlfOffs |= 4; \
370 if (oddStart) \
371 rlfOffs |= 2; \
372 if (oddEnd) \
373 rlfOffs |= 1; \
374 if (useMask) \
375 (this->*_renderLineFragmentM[rlfOffs])(dst, mask, src, start, end, pal); \
376 else \
377 (this->*_renderLineFragmentD[rlfOffs])(dst, src, start, end, pal); \
378 }
379 #else
380 #define mRenderLineFragment(hFlip, oddStart, oddEnd, useMask, dst, mask, src, start, end, pal) \
381 { \
382 if (hFlip) \
383 renderLineFragment<true>(dst, mask, src, start, end, pal); \
384 else \
385 renderLineFragment<false>(dst, mask, src, start, end, pal); \
386 }
387 #endif
388
SegaRenderer(Screen_EoB * screen)389 SegaRenderer::SegaRenderer(Screen_EoB *screen) : _screen(screen), _prioChainStart(0), _prioChainEnd(0), _pitch(64), _hScrollMode(0), _hScrollTable(0), _vScrollMode(0), _spriteTable(0), _numSpritesMax(0), _spriteMask(0)
390 #if SEGA_PERFORMANCE
391 , _renderLineFragmentD(0), _renderLineFragmentM(0)
392 #endif
393 {
394 _vram = new uint8[0x10000];
395 assert(_vram);
396 memset(_vram, 0, 0x10000 * sizeof(uint8));
397 _vsram = new uint16[40];
398 assert(_vsram);
399 memset(_vsram, 0, 40 * sizeof(uint16));
400
401 #if SEGA_PERFORMANCE
402 static const SegaRenderer::renderFuncD funcD[8] = {
403 &SegaRenderer::renderLineFragmentD<false, false, false>,
404 &SegaRenderer::renderLineFragmentD<false, false, true>,
405 &SegaRenderer::renderLineFragmentD<false, true, false>,
406 &SegaRenderer::renderLineFragmentD<false, true, true>,
407 &SegaRenderer::renderLineFragmentD<true, false, false>,
408 &SegaRenderer::renderLineFragmentD<true, false, true>,
409 &SegaRenderer::renderLineFragmentD<true, true, false>,
410 &SegaRenderer::renderLineFragmentD<true, true, true>
411 };
412
413 static const SegaRenderer::renderFuncM funcM[8] = {
414 &SegaRenderer::renderLineFragmentM<false, false, false>,
415 &SegaRenderer::renderLineFragmentM<false, false, true>,
416 &SegaRenderer::renderLineFragmentM<false, true, false>,
417 &SegaRenderer::renderLineFragmentM<false, true, true>,
418 &SegaRenderer::renderLineFragmentM<true, false, false>,
419 &SegaRenderer::renderLineFragmentM<true, false, true>,
420 &SegaRenderer::renderLineFragmentM<true, true, false>,
421 &SegaRenderer::renderLineFragmentM<true, true, true>
422 };
423
424 _renderLineFragmentD = funcD;
425 _renderLineFragmentM = funcM;
426 #endif
427
428 setResolution(320, 224);
429 }
430
~SegaRenderer()431 SegaRenderer::~SegaRenderer() {
432 delete[] _vram;
433 delete[] _vsram;
434 delete[] _spriteMask;
435 }
436
setResolution(int w,int h)437 void SegaRenderer::setResolution(int w, int h) {
438 assert(w == 320 || w == 256);
439 assert(h == 224 || h == 240);
440
441 _screenW = w;
442 _screenH = h;
443 _blocksW = w >> 3;
444 _blocksH = h >> 3;
445 _numSpritesMax = w >> 2;
446
447 delete[] _spriteMask;
448 _spriteMask = new uint8[w * h];
449 assert(_spriteMask);
450 memset(_spriteMask, 0, w * h * sizeof(uint8));
451 }
452
setPlaneTableLocation(int plane,uint16 addr)453 void SegaRenderer::setPlaneTableLocation(int plane, uint16 addr) {
454 assert(plane >= kPlaneA && plane <= kWindowPlane);
455 _planes[plane].nameTable = (uint16*)(&_vram[addr]);
456 }
457
setupPlaneAB(int pixelWidth,int pixelHeigth)458 void SegaRenderer::setupPlaneAB(int pixelWidth, int pixelHeigth) {
459 for (int i = 0; i < 2; ++i) {
460 if (pixelWidth != -1)
461 _planes[i].w = pixelWidth >> 3;
462 if (pixelHeigth != -1)
463 _planes[i].h = pixelHeigth >> 3;
464 _planes[i].mod = _planes[i].h;
465 _planes[i].nameTableSize = _planes[i].w * _planes[i].h;
466 }
467 }
468
setupWindowPlane(int blockX,int blockY,int horizontalMode,int verticalMode)469 void SegaRenderer::setupWindowPlane(int blockX, int blockY, int horizontalMode, int verticalMode) {
470 if (blockX != -1)
471 _planes[kWindowPlane].blockX = horizontalMode ? blockX : 0;
472 if (blockY != -1)
473 _planes[kWindowPlane].blockY = verticalMode ? blockY : 0;
474 _planes[kWindowPlane].w = horizontalMode ? _blocksW - blockX : blockX;
475 _planes[kWindowPlane].h = verticalMode ? _blocksH - blockY : blockY;
476 _planes[kWindowPlane].mod = _planes[kWindowPlane].blockY + _planes[kWindowPlane].h;
477 _planes[kWindowPlane].nameTableSize = _planes[kWindowPlane].w * _planes[kWindowPlane].h;
478 }
479
setHScrollTableLocation(int addr)480 void SegaRenderer::setHScrollTableLocation(int addr) {
481 assert(addr <= 0xFFFF);
482 _hScrollTable = (uint16*)(&_vram[addr]);
483 }
484
setSpriteTableLocation(int addr)485 void SegaRenderer::setSpriteTableLocation(int addr) {
486 assert(addr <= 0xFFFF);
487 _spriteTable = (uint16*)(&_vram[addr]);
488 }
489
setPitch(int pitch)490 void SegaRenderer::setPitch(int pitch) {
491 _pitch = pitch;
492 }
493
setHScrollMode(int mode)494 void SegaRenderer::setHScrollMode(int mode) {
495 _hScrollMode = mode;
496 }
497
setVScrollMode(int mode)498 void SegaRenderer::setVScrollMode(int mode) {
499 _vScrollMode = mode;
500 }
501
loadToVRAM(const void * data,uint16 dataSize,uint16 addr)502 void SegaRenderer::loadToVRAM(const void *data, uint16 dataSize, uint16 addr) {
503 assert(data);
504 assert(addr + dataSize <= 0x10000);
505 memcpy(_vram + addr, data, dataSize);
506 }
507
loadStreamToVRAM(Common::SeekableReadStream * in,uint16 addr,bool compressedData)508 void SegaRenderer::loadStreamToVRAM(Common::SeekableReadStream *in, uint16 addr, bool compressedData) {
509 assert(in);
510 uint8 *dst = _vram + addr;
511
512 if (compressedData) {
513 uint16 decodeSize = 0;
514 uint8 *data = new uint8[in->size()];
515 uint32 readSize = in->read(data, in->size());
516 decodeSize = READ_LE_UINT16(data + 2);
517 assert(decodeSize < readSize);
518 assert(decodeSize < 0x10000 - addr);
519 _screen->decodeBIN(data + 4, dst, decodeSize);
520 delete[] data;
521 } else {
522 assert(in->size() < 0x10000 - addr);
523 in->read(dst, in->size());
524 }
525 }
526
memsetVRAM(int addr,uint8 val,int len)527 void SegaRenderer::memsetVRAM(int addr, uint8 val, int len) {
528 assert(addr + len <= 0x10000);
529 memset(_vram + addr, val, len);
530 }
531
fillRectWithTiles(int vramArea,int x,int y,int w,int h,uint16 nameTblEntry,bool incr,bool topToBottom,const uint16 * patternTable)532 void SegaRenderer::fillRectWithTiles(int vramArea, int x, int y, int w, int h, uint16 nameTblEntry, bool incr, bool topToBottom, const uint16 *patternTable) {
533 uint16 addr = vramArea ? (vramArea == 1 ? 0xE000 : 0xF000) : 0xC000;
534 if (y & 0x8000) {
535 y &= ~0x8000;
536 addr = 0xE000;
537 }
538
539 uint16 *dst = (uint16*)(&_vram[addr]);
540 dst += (y * _pitch + x);
541 int ptch = _pitch - w;
542
543 assert(addr + 2 * (y * _pitch + x + h * _pitch + w) <= 0xFFFF);
544
545 if (patternTable) {
546 while (h--) {
547 const uint16 *pos = patternTable;
548 for (int i = w; i; --i)
549 *dst++ = nameTblEntry + *pos++;
550 dst += ptch;
551 patternTable += w;
552 }
553 } else if (incr) {
554 if (topToBottom) {
555 while (w--) {
556 uint16 *dst2 = dst;
557 for (int i = h; i; --i) {
558 *dst = nameTblEntry++;
559 dst += _pitch;
560 }
561 dst = ++dst2;
562 }
563 } else {
564 while (h--) {
565 for (int i = w; i; --i)
566 *dst++ = nameTblEntry++;
567 dst += ptch;
568 }
569 }
570 } else {
571 if (topToBottom) {
572 while (w--) {
573 uint16 *dst2 = dst;
574 for (int i = h; i; --i) {
575 *dst = nameTblEntry;
576 dst += _pitch;
577 }
578 dst = ++dst2;
579 }
580 } else {
581 while (h--) {
582 for (int i = w; i; --i)
583 *dst++ = nameTblEntry;
584 dst += ptch;
585 }
586 }
587 }
588 }
589
writeUint16VSRAM(int addr,uint16 value)590 void SegaRenderer::writeUint16VSRAM(int addr, uint16 value) {
591 assert(addr < 80);
592 assert(!(addr & 1));
593 _vsram[addr >> 1] = value;
594 }
595
writeUint8VRAM(int addr,uint8 value)596 void SegaRenderer::writeUint8VRAM(int addr, uint8 value) {
597 assert(addr < 0x10000);
598 _vram[addr] = value;
599 }
600
writeUint16VRAM(int addr,uint16 value)601 void SegaRenderer::writeUint16VRAM(int addr, uint16 value) {
602 assert(addr < 0x10000);
603 *((uint16*)(_vram + addr)) = value;
604 }
605
clearPlanes()606 void SegaRenderer::clearPlanes() {
607 for (int i = 0; i < 3; ++i) {
608 if (_planes[i].nameTableSize)
609 memset(_planes[i].nameTable, 0, _planes[i].nameTableSize * sizeof(uint16));
610 }
611 }
612
render(int destPageNum,int renderBlockX,int renderBlockY,int renderBlockWidth,int renderBlockHeight,bool spritesOnly)613 void SegaRenderer::render(int destPageNum, int renderBlockX, int renderBlockY, int renderBlockWidth, int renderBlockHeight, bool spritesOnly) {
614 if (renderBlockX == -1)
615 renderBlockX = 0;
616 if (renderBlockY == -1)
617 renderBlockY = 0;
618 if (renderBlockWidth == -1)
619 renderBlockWidth = _blocksW;
620 if (renderBlockHeight == -1)
621 renderBlockHeight = _blocksH;
622
623 uint8 *renderBuffer = _screen->getPagePtr(destPageNum);
624 // This also ensures that a dirty rect is created if necessary
625 _screen->fillRect(renderBlockX << 3, renderBlockY << 3, ((renderBlockX + renderBlockWidth) << 3) - 1, ((renderBlockY + renderBlockHeight) << 3) - 1, 0, destPageNum);
626
627 // Plane B
628 if (!spritesOnly)
629 renderPlanePart(kPlaneB, renderBuffer, renderBlockX, renderBlockY, renderBlockX + renderBlockWidth, renderBlockY + renderBlockHeight);
630
631 // Plane A (only draw if the nametable is not identical to that of plane B)
632 if (_planes[kPlaneA].nameTable != _planes[kPlaneB].nameTable && !spritesOnly) {
633 // If the window plane is active the rendering of plane A becomes more tedious because the window plane
634 // kind of replaces plane A in the space that is covered by it.
635 if (_planes[kWindowPlane].nameTableSize) {
636 SegaPlane *p = &_planes[kWindowPlane];
637 renderPlanePart(kPlaneA, renderBuffer, MAX<int>(0, renderBlockX), MAX<int>(0, renderBlockY), MIN<int>(p->blockX, renderBlockX + renderBlockWidth), MIN<int>(_blocksH, renderBlockY + renderBlockHeight));
638 renderPlanePart(kPlaneA, renderBuffer, MAX<int>(0, renderBlockX), MAX<int>(0, renderBlockY), MIN<int>(_blocksW, renderBlockX + renderBlockWidth), MIN<int>(p->blockY, renderBlockY + renderBlockHeight));
639 renderPlanePart(kPlaneA, renderBuffer, MAX<int>(p->blockX + p->w, renderBlockX), MAX<int>(0, renderBlockY), MIN<int>(_blocksW, renderBlockX + renderBlockWidth), MIN<int>(_blocksH, renderBlockY + renderBlockHeight));
640 renderPlanePart(kPlaneA, renderBuffer, MAX<int>(0, renderBlockX), MAX<int>(p->blockY + p->h, renderBlockY), MIN<int>(_blocksW, renderBlockX + renderBlockWidth), MIN<int>(_blocksH, renderBlockY + renderBlockHeight));
641 } else {
642 renderPlanePart(kPlaneA, renderBuffer, renderBlockX, renderBlockY, renderBlockX + renderBlockWidth, renderBlockY + renderBlockHeight);
643 }
644 }
645
646 // Window Plane
647 if (_planes[kWindowPlane].nameTableSize && !spritesOnly) {
648 SegaPlane *p = &_planes[kWindowPlane];
649 renderPlanePart(kWindowPlane, renderBuffer, MIN<int>(p->blockX, renderBlockX + renderBlockWidth), MIN<int>(p->blockY, renderBlockY + renderBlockHeight), MAX<int>(p->blockX + p->w, renderBlockX), MAX<int>(p->blockY + p->h, renderBlockY));
650 }
651
652 // Sprites
653 memset(_spriteMask, 0xFF, _screenW * _screenH * sizeof(uint8));
654 const uint16 *pos = _spriteTable;
655 for (int i = 0; i < _numSpritesMax && pos; ++i) {
656 int y = *pos++ & 0x3FF;
657 uint8 bH = ((*pos >> 8) & 3) + 1;
658 uint8 bW = ((*pos >> 10) & 3) + 1;
659 uint8 next = *pos++ & 0x7F;
660 uint16 pal = ((*pos >> 13) & 3) << 4;
661 bool prio = (*pos & 0x8000);
662 bool hflip = (*pos & 0x800);
663 bool vflip = (*pos & 0x1000);
664 uint16 tile = *pos++ & 0x7FF;
665 int x = *pos & 0x3FF;
666
667 // Sprite masking. Can't happen really, since the animator automatically adds 128 to x and y coords for all sprites.
668 assert(!(x == 0 && y >= 128));
669
670 assert(!hflip);
671 assert(!vflip);
672
673 x -= 128;
674 y -= 128;
675
676 /*if ((x >> 3) < renderBlockX) {
677 bW = MIN<int>(0, (int)bW - (renderBlockX - (x >> 3)));
678 x = (renderBlockX << 3);
679
680 }
681
682 if ((y >> 3) < renderBlockY) {
683 bH = MIN<int>(0, (int)bH - (renderBlockY - (y >> 3)));
684 y = (renderBlockY << 3);
685 }
686
687 bW = MIN<int>(bW, renderBlockWidth);
688 bH = MIN<int>(bH, renderBlockHeight);*/
689
690 uint8 *dst = renderBuffer + y * _screenW + x;
691 uint8 *msk = _spriteMask + y * _screenW + x;
692
693 for (int blX = 0; blX < bW; ++blX) {
694 uint8 *dst2 = dst;
695 uint8 *msk2 = msk;
696 for (int blY = 0; blY < bH; ++blY) {
697 renderSpriteTile(dst, msk, x + (blX << 3), y + (blY << 3), tile++, pal, vflip, hflip, prio);
698 dst += (_screenW << 3);
699 msk += (_screenW << 3);
700 }
701 dst = dst2 + 8;
702 msk = msk2 + 8;
703 }
704
705 pos = next ? &_spriteTable[next << 2] : 0;
706 }
707
708 // Priority Tiles
709 // Instead of going through all rendering passes for all planes again (only now drawing the
710 // prio tiles instead of the non-priority tiles) I have collected the data for the priority
711 // tiles on the way and put that data into a chain. Should be faster...
712 for (PrioTileRenderObj *e = _prioChainStart; e; e = e->_next)
713 mRenderLineFragment(e->_hflip, e->_start & 1, e->_end & 1, e->_mask, e->_dst, e->_mask, e->_src, e->_start, e->_end, e->_pal)
714
715 clearPrioChain();
716 }
717
renderPlanePart(int plane,uint8 * dstBuffer,int x1,int y1,int x2,int y2)718 void SegaRenderer::renderPlanePart(int plane, uint8 *dstBuffer, int x1, int y1, int x2, int y2) {
719 SegaPlane *p = &_planes[plane];
720 uint8 *dst = dstBuffer + (y1 << 3) * _screenW + (x1 << 3);
721
722 for (int y = y1; y < y2; ++y) {
723 int hScrollTableIndex = (plane == kWindowPlane) ? -1 : (_hScrollMode == kHScrollFullScreen) ? plane : (y1 << 4) + plane;
724 uint8 *dst2 = dst;
725 for (int x = x1; x < x2; ++x) {
726 int vScrollTableIndex = (plane == kWindowPlane) ? -1 : (_vScrollMode == kVScrollFullScreen) ? plane : (x & ~1) + plane;
727 uint16 vscrNt = 0;
728 uint16 vscrPxStart = 0;
729 uint16 vscrPxEnd = 8;
730
731 if (vScrollTableIndex != -1) {
732 vscrNt = _vsram[vScrollTableIndex] & 0x3FF;
733 vscrPxStart = vscrNt & 7;
734 vscrNt >>= 3;
735 }
736
737 int ty = (vscrNt + y) % p->mod;
738
739 renderPlaneTile(dst, x, &p->nameTable[ty * _pitch], vscrPxStart, vscrPxEnd, hScrollTableIndex, _pitch);
740
741 if (vscrPxStart) {
742 ty = (ty + 1) % p->mod;
743 uint16 dstOffs = (vscrPxEnd - vscrPxStart) * _screenW;
744 vscrPxEnd = vscrPxStart;
745 vscrPxStart = 0;
746 renderPlaneTile(dst + dstOffs, x, &p->nameTable[ty * _pitch], vscrPxStart, vscrPxEnd, hScrollTableIndex, _pitch);
747 }
748 dst += 8;
749 }
750 dst = dst2 + (_screenW << 3);
751 }
752 }
753
renderPlaneTile(uint8 * dst,int ntblX,const uint16 * ntblLine,int vScrollLSBStart,int vScrollLSBEnd,int hScrollTableIndex,uint16 pitch)754 void SegaRenderer::renderPlaneTile(uint8 *dst, int ntblX, const uint16 *ntblLine, int vScrollLSBStart, int vScrollLSBEnd, int hScrollTableIndex, uint16 pitch) {
755 for (int bY = vScrollLSBStart; bY < vScrollLSBEnd; ++bY) {
756 uint8 *dst2 = dst;
757 uint16 hscrNt = 0;
758 uint16 hscrPx = 0;
759
760 if (hScrollTableIndex != -1) {
761 hscrNt = (-_hScrollTable[hScrollTableIndex]) & 0x3FF;
762 hscrPx = hscrNt & 7;
763 hscrNt >>= 3;
764 }
765
766 const uint16 *pNt = &ntblLine[(ntblX + hscrNt) % pitch];
767 if (pNt < (const uint16*)(&_vram[0x10000])) {
768 uint16 nt = *pNt;
769 uint16 pal = ((nt >> 13) & 3) << 4;
770 bool hflip = (nt & 0x800);
771 int y = bY % 8;
772 if (nt & 0x1000) // vflip
773 y = 7 - y;
774
775 // We skip the priority tiles here and draw them later
776 if (nt & 0x8000)
777 initPrioRenderTask(dst, 0, &_vram[((nt & 0x7FF) << 5) + (y << 2) + (hscrPx >> 1)], hscrPx, 8, pal, hflip);
778 else
779 mRenderLineFragment(hflip, hscrPx & 1, 0, 0, dst, 0, &_vram[((nt & 0x7FF) << 5) + (y << 2) + (hscrPx >> 1)], hscrPx, 8, pal);
780 }
781
782 if (hscrPx) {
783 dst += (8 - hscrPx);
784 pNt = &ntblLine[(ntblX + hscrNt + 1) % pitch];
785 if (pNt < (const uint16*)(&_vram[0x10000])) {
786 uint16 nt = *pNt;
787 uint16 pal = ((nt >> 13) & 3) << 4;
788 bool hflip = (nt & 0x800);
789 int y = bY % 8;
790 if (nt & 0x1000) // vflip
791 y = 7 - y;
792
793 // We skip the priority tiles here and draw them later
794 if (nt & 0x8000)
795 initPrioRenderTask(dst, 0, &_vram[((nt & 0x7FF) << 5) + (y << 2)], 0, hscrPx, pal, hflip);
796 else
797 mRenderLineFragment(hflip, 0, hscrPx & 1, 0, dst, 0, &_vram[((nt & 0x7FF) << 5) + (y << 2)], 0, hscrPx, pal)
798 }
799 }
800
801 if (hScrollTableIndex != -1 && _hScrollMode == kHScroll1PixelRows)
802 hScrollTableIndex += 2;
803 dst = dst2 + _screenW;
804 }
805 }
806
807 #undef vflip
808
renderSpriteTile(uint8 * dst,uint8 * mask,int x,int y,uint16 tile,uint8 pal,bool vflip,bool hflip,bool prio)809 void SegaRenderer::renderSpriteTile(uint8 *dst, uint8 *mask, int x, int y, uint16 tile, uint8 pal, bool vflip, bool hflip, bool prio) {
810 if (y <= -8 || y >= _screenH || x <= -8 || x >= _screenW)
811 return;
812
813 const uint8 *src = &_vram[tile << 5];
814 if (vflip)
815 src += 31;
816
817 if (y < 0) {
818 dst -= (y * _screenW);
819 mask -= (y * _screenW);
820 } if (x < 0) {
821 dst -= x;
822 mask -= x;
823 }
824
825 int xstart = CLIP<int>(-x, 0, 7);
826 int xend = CLIP<int>(_screenW - x, 0, 8);
827 src += (xstart >> 1);
828
829 int ystart = CLIP<int>(-y, 0, 7);
830 int yend = CLIP<int>(_screenH - y, 0, 8);
831 src += (ystart << 2);
832
833 for (int bY = ystart; bY < yend; ++bY) {
834 uint8 *dst2 = dst;
835 uint8 *msk2 = mask;
836
837 if (prio)
838 initPrioRenderTask(dst, mask, src, xstart, xend, pal, hflip);
839 else
840 mRenderLineFragment(hflip, xstart & 1, xend & 1, 1, dst, mask, src, xstart, xend, pal);
841
842 src += 4;
843 dst = dst2 + _screenW;
844 mask = msk2 + _screenW;
845 }
846 }
847
848 #if SEGA_PERFORMANCE
renderLineFragmentM(uint8 * dst,uint8 * mask,const uint8 * src,int start,int end,uint8 pal)849 template<bool hflip, bool oddStart, bool oddEnd> void SegaRenderer::renderLineFragmentM(uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal) {
850 if (hflip)
851 src += ((end - 1 - start) >> 1);
852
853 for (int i = (end - start) >> 1; i; --i) {
854 uint8 col = hflip ? (oddEnd ? *src-- >> 4 : *src & 0x0F) : (oddStart ? *src++ & 0x0F : *src >> 4);
855 uint8 col2 = hflip ? (oddEnd ? *src & 0x0F : *src-- >> 4) : (oddStart ? *src >> 4 : *src++ & 0x0F);
856 if (col & *mask) {
857 *dst = pal | col;
858 *mask = 0;
859 }
860 dst++;
861 mask++;
862 if (col2 & *mask) {
863 *dst = pal | col2;
864 *mask = 0;
865 }
866 dst++;
867 mask++;
868 }
869 if (oddStart != oddEnd) {
870 uint8 col = hflip ? (oddEnd ? *src-- >> 4 : *src & 0x0F) : (oddStart ? *src++ & 0x0F : *src >> 4);
871 if (col & *mask) {
872 *dst = pal | col;
873 *mask = 0;
874 }
875 dst++;
876 mask++;
877 }
878 }
879
renderLineFragmentD(uint8 * dst,const uint8 * src,int start,int end,uint8 pal)880 template<bool hflip, bool oddStart, bool oddEnd> void SegaRenderer::renderLineFragmentD(uint8 *dst, const uint8 *src, int start, int end, uint8 pal) {
881 if (hflip)
882 src += ((end - 1 - start) >> 1);
883
884 for (int i = (end - start) >> 1; i; --i) {
885 uint8 col = hflip ? (oddEnd ? *src-- >> 4 : *src & 0x0F) : (oddStart ? *src++ & 0x0F : *src >> 4);
886 uint8 col2 = hflip ? (oddEnd ? *src & 0x0F : *src-- >> 4) : (oddStart ? *src >> 4 : *src++ & 0x0F);
887 if (col)
888 *dst = pal | col;
889 dst++;
890 if (col2)
891 *dst = pal | col2;
892 dst++;
893 }
894 if (oddStart != oddEnd) {
895 uint8 col = hflip ? (oddEnd ? *src-- >> 4 : *src & 0x0F) : (oddStart ? *src++ & 0x0F : *src >> 4);
896 if (col)
897 *dst = pal | col;
898 dst++;
899 }
900 }
901 #else
renderLineFragment(uint8 * dst,uint8 * mask,const uint8 * src,int start,int end,uint8 pal)902 template<bool hflip> void SegaRenderer::renderLineFragment(uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal) {
903 if (hflip) {
904 src += ((end - 1 - start) >> 1);
905 if (end & 1) {
906 start++;
907 end++;
908 }
909 }
910
911 if (mask) {
912 for (int bX = start; bX < end; ++bX) {
913 uint8 col = hflip ? ((bX & 1) ? *src-- >> 4 : *src & 0x0F) : ((bX & 1) ? *src++ & 0x0F : *src >> 4);
914 if (col & *mask) {
915 *dst = pal | col;
916 *mask = 0;
917 }
918 dst++;
919 mask++;
920 }
921 } else {
922 for (int bX = start; bX < end; ++bX) {
923 uint8 col = hflip ? ((bX & 1) ? *src-- >> 4 : *src & 0x0F) : ((bX & 1) ? *src++ & 0x0F : *src >> 4);
924 if (col)
925 *dst = pal | col;
926 dst++;
927 }
928 }
929 }
930 #endif
931
932 #undef mRenderLineFragment
933
initPrioRenderTask(uint8 * dst,uint8 * mask,const uint8 * src,int start,int end,uint8 pal,bool hflip)934 void SegaRenderer::initPrioRenderTask(uint8 *dst, uint8 *mask, const uint8 *src, int start, int end, uint8 pal, bool hflip) {
935 #if SEGA_USE_MEMPOOL
936 _prioChainEnd = new (_prioRenderMemPool) PrioTileRenderObj(_prioChainEnd, dst, mask, src, start, end, pal, hflip);
937 #else
938 _prioChainEnd = new PrioTileRenderObj(_prioChainEnd, dst, mask, src, start, end, pal, hflip);
939 #endif
940 if (!_prioChainStart)
941 _prioChainStart = _prioChainEnd;
942 }
943
clearPrioChain()944 void SegaRenderer::clearPrioChain() {
945 while (_prioChainEnd) {
946 _prioChainEnd->_next = 0;
947 PrioTileRenderObj *e = _prioChainEnd->_pred;
948 #if SEGA_USE_MEMPOOL
949 _prioRenderMemPool.deleteChunk(_prioChainEnd);
950 #else
951 delete _prioChainEnd;
952 #endif
953 _prioChainEnd = e;
954 }
955 _prioChainStart = 0;
956 }
957
SegaAnimator(SegaRenderer * renderer)958 SegaAnimator::SegaAnimator(SegaRenderer *renderer) : _renderer(renderer), _needUpdate(false) {
959 _sprites = new Sprite[80];
960 assert(_sprites);
961 memset(_sprites, 0, sizeof(Sprite) * 80);
962 _tempBuffer = new uint16[320];
963 assert(_tempBuffer);
964 memset(_tempBuffer, 0, sizeof(uint16) * 320);
965 int linkCnt = 1;
966 for (int i = 1; i < 317; i += 4)
967 _tempBuffer[i] = linkCnt++;
968 clearSprites();
969 _renderer->memsetVRAM(0xDC00, 0, 0x400);
970 }
971
~SegaAnimator()972 SegaAnimator::~SegaAnimator() {
973 delete[] _sprites;
974 delete[] _tempBuffer;
975 }
976
initSprite(int id,int16 x,int16 y,uint16 nameTbl,uint16 hw)977 void SegaAnimator::initSprite(int id, int16 x, int16 y, uint16 nameTbl, uint16 hw) {
978 assert(id < 80);
979 Sprite &s = _sprites[id];
980 s.x = x;
981 s.y = y;
982 s.nameTbl = nameTbl;
983 s.hw = hw;
984 _needUpdate = true;
985 }
986
clearSprites()987 void SegaAnimator::clearSprites() {
988 for (Sprite *s = _sprites; s != &_sprites[80]; ++s)
989 s->x = 0x4000;
990 _needUpdate = true;
991 }
992
moveMorphSprite(int id,uint16 nameTbl,int16 addX,int16 addY)993 void SegaAnimator::moveMorphSprite(int id, uint16 nameTbl, int16 addX, int16 addY) {
994 assert(id < 80);
995 Sprite &s = _sprites[id];
996 s.x += addX;
997 s.y += addY;
998 s.nameTbl = nameTbl;
999 _needUpdate = true;
1000 }
1001
moveSprites(int id,uint16 num,int16 addX,int16 addY)1002 void SegaAnimator::moveSprites(int id, uint16 num, int16 addX, int16 addY) {
1003 assert(id < 80);
1004 Sprite *s = &_sprites[id];
1005 while (num--) {
1006 s->x += addX;
1007 s->y += addY;
1008 s++;
1009 }
1010 _needUpdate = true;
1011 }
1012
moveSprites2(int id,uint16 num,int16 addX,int16 addY)1013 void SegaAnimator::moveSprites2(int id, uint16 num, int16 addX, int16 addY) {
1014 assert(id < 80);
1015 Sprite *s = &_sprites[id];
1016 uint16 sbx = s->x;
1017 uint16 sby = s->y;
1018 while (num--) {
1019 s->x = s->x - sbx + addX;
1020 s->y = s->y - sby + addY;
1021 s++;
1022 }
1023 _needUpdate = true;
1024 }
1025
update()1026 void SegaAnimator::update() {
1027 if (!_needUpdate)
1028 return;
1029
1030 uint16 *dst = _tempBuffer;
1031 for (Sprite *s = _sprites; s != &_sprites[80]; ++s) {
1032 if (s->x == 0x4000)
1033 continue;
1034 *dst++ = (uint16)(s->y + 128);
1035 *dst = (*dst & 0xFF) | (s->hw << 8);
1036 dst++;
1037 *dst++ = s->nameTbl;
1038 *dst++ = (uint16)(s->x + 128);
1039 }
1040
1041 for (; dst < &_tempBuffer[320]; dst += 4)
1042 *dst = 0;
1043
1044 _renderer->loadToVRAM(_tempBuffer, 640, 0xDC00);
1045 _needUpdate = false;
1046 }
1047
SegaCDFont(Common::Language lang,const uint16 * convTable1,const uint16 * convTable2,const uint8 * widthTable1,const uint8 * widthTable2,const uint8 * widthTable3)1048 SegaCDFont::SegaCDFont(Common::Language lang, const uint16 *convTable1, const uint16 *convTable2, const uint8 *widthTable1, const uint8 *widthTable2, const uint8 *widthTable3) : Font(),
1049 _lang(lang), _style(0), _forceTwoByte(false), _forceOneByte(false), _convTable1(convTable1), _convTable2(convTable2), _widthTable1(widthTable1), _widthTable2(widthTable2),
1050 _widthTable3(widthTable3), _buffer(0), _data(0), _colorMap(0), _width(12), _height(12) {
1051 }
1052
~SegaCDFont()1053 SegaCDFont::~SegaCDFont() {
1054 delete[] _buffer;
1055 }
1056
load(Common::SeekableReadStream & file)1057 bool SegaCDFont::load(Common::SeekableReadStream &file) {
1058 uint32 size = file.size();
1059 if (!size)
1060 return false;
1061
1062 delete[] _buffer;
1063 uint8 *newData = new uint8[size];
1064 file.read(newData, size);
1065 _buffer = newData;
1066 _data = _buffer;
1067 if (_lang == Common::EN_ANY)
1068 _data += 131072;
1069 else if (_lang != Common::JA_JPN)
1070 error("SegaCDFont::load(): Unsupported language");
1071
1072 return true;
1073 }
1074
getCharWidth(uint16 c) const1075 int SegaCDFont::getCharWidth(uint16 c) const {
1076 uint8 charWidth, charHeight, charPitch;
1077 getGlyphData(c, charWidth, charHeight, charPitch);
1078 return charWidth;
1079 }
1080
getCharHeight(uint16 c) const1081 int SegaCDFont::getCharHeight(uint16 c) const {
1082 uint8 charWidth, charHeight, charPitch;
1083 getGlyphData(c, charWidth, charHeight, charPitch);
1084 return charHeight;
1085 }
1086
setStyles(int styles)1087 void SegaCDFont::setStyles(int styles) {
1088 assert(_buffer);
1089 _forceTwoByte = (styles & kStyleFullWidth);
1090 _forceOneByte = (styles & kStyleForceOneByte);
1091 _style = (styles & kStyleNarrow1) ? 1 : (styles & kStyleNarrow2 ? 2 : 0);
1092 }
1093
drawChar(uint16 c,byte * dst,int pitch,int xOffs,int yOffs) const1094 void SegaCDFont::drawChar(uint16 c, byte *dst, int pitch, int xOffs, int yOffs) const {
1095 uint8 charWidth, charHeight, charPitch;
1096
1097 const uint8 *pos = getGlyphData(c, charWidth, charHeight, charPitch);
1098 uint8 p = (xOffs & 1) ? 0x0F : 0xF0;
1099 uint8 color1 = _colorMap[1];
1100
1101 color1 &= p;
1102 p = ~p;
1103
1104 for (int y = 0; y < charHeight; ++y) {
1105 c = *pos++ << 8;
1106 if (charPitch != 8) {
1107 c |= *pos;
1108 if (y & 1) {
1109 c <<= 4;
1110 pos++;
1111 }
1112
1113 }
1114 uint8 *dst2 = dst;
1115 for (int x = xOffs; x < charPitch + xOffs; ++x) {
1116 if (c & 0x8000)
1117 *dst = (*dst & p) | color1;
1118 c <<= 1;
1119 p = ~p;
1120 color1 = (color1 << 4) | (color1 >> 4);
1121 if (x & 1)
1122 dst++;
1123 if ((x & 7) == 7)
1124 dst += 28;
1125 }
1126 dst = dst2 + 4;
1127 if ((++yOffs & 7) == 0)
1128 dst = dst + (pitch << 5) - 32;
1129 }
1130 }
1131
getGlyphData(uint16 c,uint8 & charWidth,uint8 & charHeight,uint8 & pitch) const1132 const uint8 *SegaCDFont::getGlyphData(uint16 c, uint8 &charWidth, uint8 &charHeight, uint8 &pitch) const {
1133 const uint8 *res = 0;
1134 uint16 lo = 0;
1135 uint16 hi = 0;
1136
1137 if (c == 0 || c == 13) {
1138 charWidth = charHeight = pitch = 0;
1139 return 0;
1140 }
1141
1142 if (c < 256) {
1143 if (_forceTwoByte) {
1144 assert(c >= 32 && c < 224);
1145 c = _convTable2[c - 32];
1146 hi = c >> 8;
1147 lo = c & 0xFF;
1148 } else {
1149 if (c < 128) {
1150 if (_lang != Common::JA_JPN && c >= 96)
1151 c += 96;
1152 else
1153 c -= 32;
1154 if (c & 0xF000)
1155 c = 0;
1156 } else {
1157 if (c >= 224)
1158 c -= 64;
1159 else if (c >= 160)
1160 c -= 96;
1161 }
1162 charWidth = charHeight = pitch = 8;
1163 return &_data[c << 3];
1164 }
1165 } else {
1166 lo = c >> 8;
1167 hi = c & 0xFF;
1168 }
1169
1170 if (lo > 0x9E) {
1171 if (hi > 0x9F)
1172 hi -= 176;
1173 else
1174 hi -= 112;
1175 hi <<= 1;
1176 lo -= 126;
1177 } else {
1178 if (hi > 0x9F)
1179 hi -= 177;
1180 else
1181 hi -= 113;
1182 hi = (hi << 1) + 1;
1183 lo -= 31;
1184 if (lo >= 97)
1185 lo -= 1;
1186 }
1187
1188 c = (hi << 8) | lo;
1189
1190 if (c >= 0x5000)
1191 c = 0x2121;
1192
1193 c -= _convTable1[(c >> 8) - 32];
1194
1195 int vrnt = 0;
1196 if (c >= 376 || _style == 0)
1197 vrnt = 0;
1198 else if (_style == 1 || c < 188 || c >= 282)
1199 vrnt = 1;
1200
1201 if (vrnt == 0) {
1202 charWidth = (_lang != Common::JA_JPN && (c < 188)) ? _widthTable1[c] : 12;
1203 charHeight = pitch = 12;
1204 res = &_data[0x19A0 + 18 * c];
1205 } else if (_lang == Common::JA_JPN) {
1206 charWidth = pitch = 8;
1207 charHeight = 12;
1208 res = &_data[0x800 + 12 * c];
1209 } else if (_style == 2) {
1210 charWidth = (c < 188) ? _widthTable3[c] : 8;
1211 charHeight = pitch = 12;
1212 res = &_data[0x3410 + 18 * c];
1213 } else {
1214 charWidth = (c < 188) ? _widthTable2[c] : 8;
1215 charHeight = 12;
1216 pitch = 8;
1217 res = &_data[0x800 + 12 * c];
1218 }
1219
1220 return res;
1221 }
1222
ScrollManager(SegaRenderer * renderer)1223 ScrollManager::ScrollManager(SegaRenderer *renderer) : _renderer(renderer) {
1224 _vScrollTimers = new ScrollTimer[2];
1225 assert(_vScrollTimers);
1226 _hScrollTimers = new ScrollTimer[2];
1227 assert(_hScrollTimers);
1228 }
1229
~ScrollManager()1230 ScrollManager::~ScrollManager() {
1231 delete[] _vScrollTimers;
1232 delete[] _hScrollTimers;
1233 }
1234
setVScrollTimers(uint16 destA,int incrA,int delayA,uint16 destB,int incrB,int delayB)1235 void ScrollManager::setVScrollTimers(uint16 destA, int incrA, int delayA, uint16 destB, int incrB, int delayB) {
1236 _vScrollTimers[0]._offsDest = destA;
1237 _vScrollTimers[0]._incr = incrA;
1238 _vScrollTimers[0]._timer = _vScrollTimers[0]._delay = delayA;
1239 _vScrollTimers[1]._offsDest = destB;
1240 _vScrollTimers[1]._incr = incrB;
1241 _vScrollTimers[1]._timer = _vScrollTimers[1]._delay = delayB;
1242 }
1243
setHScrollTimers(uint16 destA,int incrA,int delayA,uint16 destB,int incrB,int delayB)1244 void ScrollManager::setHScrollTimers(uint16 destA, int incrA, int delayA, uint16 destB, int incrB, int delayB) {
1245 _hScrollTimers[0]._offsDest = destA;
1246 _hScrollTimers[0]._incr = incrA;
1247 _hScrollTimers[0]._timer = _hScrollTimers[0]._delay = delayA;
1248 _hScrollTimers[1]._offsDest = destB;
1249 _hScrollTimers[1]._incr = incrB;
1250 _hScrollTimers[1]._timer = _hScrollTimers[1]._delay = delayB;
1251 }
1252
updateScrollTimers()1253 void ScrollManager::updateScrollTimers() {
1254 for (int i = 0; i < 4; ++i) {
1255 ScrollTimer &t = i < 2 ? _vScrollTimers[i] : _hScrollTimers[i - 2];
1256 if (t._delay == 0 && t._offsCur != t._offsDest)
1257 t._offsCur = t._offsDest;
1258 if (t._offsCur == t._offsDest)
1259 continue;
1260 if (--t._timer)
1261 continue;
1262
1263 t._offsCur += t._incr;
1264 t._timer = t._delay;
1265 }
1266
1267 _renderer->writeUint16VSRAM(0, _vScrollTimers[0]._offsCur);
1268 _renderer->writeUint16VSRAM(2, _vScrollTimers[1]._offsCur);
1269 _renderer->writeUint16VRAM(0xD800, _hScrollTimers[0]._offsCur);
1270 _renderer->writeUint16VRAM(0xD802, _hScrollTimers[1]._offsCur);
1271 }
1272
fastForward()1273 void ScrollManager::fastForward() {
1274 _renderer->writeUint16VSRAM(0, _vScrollTimers[0]._offsDest);
1275 _renderer->writeUint16VSRAM(2, _vScrollTimers[1]._offsDest);
1276 _renderer->writeUint16VRAM(0xD800, _hScrollTimers[0]._offsDest);
1277 _renderer->writeUint16VRAM(0xD802, _hScrollTimers[1]._offsDest);
1278 }
1279
1280 } // End of namespace Kyra
1281
1282 #endif // ENABLE_EOB
1283