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 #include "bladerunner/zbuffer.h"
24 
25 #include "bladerunner/decompress_lzo.h"
26 
27 #include "common/debug.h"
28 
29 namespace BladeRunner {
30 
reset()31 void ZBufferDirtyRects::reset() {
32 	_count = 0;
33 }
34 
add(Common::Rect rect)35 bool ZBufferDirtyRects::add(Common::Rect rect) {
36 	if (_count == MAX_DIRTY_RECTS)
37 		return false;
38 
39 	_rects[_count++] = rect;
40 	if (_count > 1) {
41 		extendExisting();
42 	}
43 	return true;
44 }
45 
extendExisting()46 void ZBufferDirtyRects::extendExisting() {
47 	if (_count < 2)
48 		return;
49 
50 	Common::Rect last = _rects[_count - 1];
51 
52 	int i;
53 	for (i = 0; i != _count - 1; ++i) {
54 		if (last.intersects(_rects[i])) {
55 			_rects[i].extend(last);
56 			--_count;
57 			break;
58 		}
59 	}
60 }
61 
getCount() const62 int ZBufferDirtyRects::getCount() const {
63 	return _count;
64 }
65 
popRect(Common::Rect * rect)66 bool ZBufferDirtyRects::popRect(Common::Rect *rect) {
67 	if (_count == 0)
68 		return false;
69 
70 	*rect = _rects[--_count];
71 	return true;
72 }
73 
ZBuffer()74 ZBuffer::ZBuffer() {
75 	_zbuf1 = nullptr;
76 	_zbuf2 = nullptr;
77 	_dirtyRects = new ZBufferDirtyRects();
78 	_width = 0;
79 	_height = 0;
80 	enable();
81 }
82 
~ZBuffer()83 ZBuffer::~ZBuffer() {
84 	delete[] _zbuf2;
85 	delete[] _zbuf1;
86 	delete _dirtyRects;
87 }
88 
init(int width,int height)89 void ZBuffer::init(int width, int height) {
90 	_width = width;
91 	_height = height;
92 
93 	_zbuf1 = new uint16[width * height];
94 	_zbuf2 = new uint16[width * height];
95 }
96 
decodePartialZBuffer(const uint8 * src,uint16 * curZBUF,uint32 srcLen)97 static int decodePartialZBuffer(const uint8 *src, uint16 *curZBUF, uint32 srcLen) {
98 	uint32 dstSize = 640 * 480; // This is taken from global variables?
99 	uint32 dstRemain = dstSize;
100 
101 	uint16 *curzp = curZBUF;
102 	const uint16 *inp = (const uint16 *)src;
103 
104 	while (dstRemain && (inp - (const uint16 *)src) < (ptrdiff_t)srcLen) {
105 		uint32 count = FROM_LE_16(*inp++);
106 
107 		if (count & 0x8000) {
108 			count = MIN(count & 0x7fff, dstRemain);
109 			dstRemain -= count;
110 
111 			while (count--) {
112 				uint16 value = FROM_LE_16(*inp++);
113 				if (value)
114 					*curzp = value;
115 				++curzp;
116 			}
117 		} else {
118 			count = MIN(count, dstRemain);
119 			dstRemain -= count;
120 			uint16 value = FROM_LE_16(*inp++);
121 
122 			if (!value) {
123 				curzp += count;
124 			} else {
125 				while (count--)
126 					*curzp++ = value;
127 			}
128 		}
129 	}
130 	return dstSize - dstRemain;
131 }
132 
decodeData(const uint8 * data,int size)133 bool ZBuffer::decodeData(const uint8 *data, int size) {
134 	if (_disabled) {
135 		return false;
136 	}
137 
138 	uint32 width, height, complete;// , unk0;
139 
140 	width    = READ_LE_UINT32(data + 0);
141 	height   = READ_LE_UINT32(data + 4);
142 	complete = READ_LE_UINT32(data + 8);
143 	/*unk0 =*/ READ_LE_UINT32(data + 12);
144 
145 	if (width != (uint32)_width || height != (uint32)_height) {
146 		warning("zbuffer size mismatch (%d, %d) != (%d, %d)", _width, _height, width, height);
147 		return false;
148 	}
149 
150 	data += 16;
151 	size -= 16;
152 
153 	if (complete) {
154 		resetUpdates();
155 		size_t zbufOutSize;
156 		decompress_lzo1x(data, size, (uint8 *)_zbuf1, &zbufOutSize);
157 #ifdef SCUMM_BIG_ENDIAN
158 		// As the compression is working with 8-bit data, on big-endian architectures we have to switch order of bytes in uncompressed data
159 		uint8 *rawZbuf = (uint8 *)_zbuf1;
160 		for (size_t i = 0; i < zbufOutSize - 1; i += 2) {
161 			SWAP(rawZbuf[i], rawZbuf[i + 1]);
162 		}
163 #endif
164 		memcpy(_zbuf2, _zbuf1, 2 * _width * _height);
165 	} else {
166 		clean();
167 		decodePartialZBuffer(data, _zbuf1, size);
168 		decodePartialZBuffer(data, _zbuf2, size);
169 	}
170 
171 	return true;
172 }
173 
getData() const174 uint16 *ZBuffer::getData() const {
175 	return _zbuf2;
176 }
177 
getZValue(int x,int y) const178 uint16 ZBuffer::getZValue(int x, int y) const {
179 	assert(x >= 0 && x < _width);
180 	assert(y >= 0 && y < _height);
181 
182 	if (_zbuf2 == nullptr) {
183 		return 0;
184 	}
185 
186 	return _zbuf2[y * _width + x];
187 }
188 
blit(Common::Rect rect)189 void ZBuffer::blit(Common::Rect rect) {
190 	int line_width = rect.width();
191 
192 	for (int y = rect.top; y != rect.bottom; ++y) {
193 		int offset = y * _width + rect.left;
194 		memcpy(_zbuf2 + offset, _zbuf1 + offset, 2 * line_width);
195 	}
196 }
197 
mark(Common::Rect rect)198 void ZBuffer::mark(Common::Rect rect) {
199 	assert(rect.isValidRect());
200 
201 	// debug("mark %d, %d, %d, %d", rect.top, rect.right, rect.bottom, rect.left);
202 	rect.clip(_width, _height);
203 	_dirtyRects->add(rect);
204 }
205 
clean()206 void ZBuffer::clean() {
207 	Common::Rect rect;
208 	while (_dirtyRects->popRect(&rect)) {
209 		// debug("blit %d, %d, %d, %d", rect.top, rect.right, rect.bottom, rect.left);
210 		blit(rect);
211 	}
212 }
213 
resetUpdates()214 void ZBuffer::resetUpdates() {
215 	_dirtyRects->reset();
216 }
217 
disable()218 void ZBuffer::disable() {
219 	_disabled = true;
220 }
221 
enable()222 void ZBuffer::enable() {
223 	_disabled = false;
224 }
225 
226 } // End of namespace BladeRunner
227