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