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 "kyra/graphics/wsamovie.h"
24 #include "kyra/resource/resource.h"
25 
26 #include "common/endian.h"
27 
28 namespace Kyra {
29 
WSAMovie_v1(KyraEngine_v1 * vm)30 WSAMovie_v1::WSAMovie_v1(KyraEngine_v1 *vm)
31 	: Movie(vm), _frameData(0), _frameOffsTable(0), _offscreenBuffer(0), _deltaBuffer(0) {
32 }
33 
~WSAMovie_v1()34 WSAMovie_v1::~WSAMovie_v1() {
35 	close();
36 }
37 
open(const char * filename,int offscreenDecode,Palette * palBuf)38 int WSAMovie_v1::open(const char *filename, int offscreenDecode, Palette *palBuf) {
39 	close();
40 
41 	uint32 flags = 0;
42 	uint32 fileSize;
43 	uint8 *p = _vm->resource()->fileData(filename, &fileSize);
44 	if (!p)
45 		return 0;
46 
47 	const uint8 *wsaData = p;
48 	_numFrames = READ_LE_UINT16(wsaData); wsaData += 2;
49 	_width = READ_LE_UINT16(wsaData); wsaData += 2;
50 	_height = READ_LE_UINT16(wsaData); wsaData += 2;
51 	_deltaBufferSize = READ_LE_UINT16(wsaData); wsaData += 2;
52 	_offscreenBuffer = NULL;
53 	_flags = 0;
54 	if (_vm->gameFlags().useAltShapeHeader) {
55 		flags = READ_LE_UINT16(wsaData);
56 		wsaData += 2;
57 	}
58 
59 	uint32 offsPal = 0;
60 	if (flags & 1) {
61 		offsPal = 0x300;
62 		_flags |= WF_HAS_PALETTE;
63 		if (palBuf)
64 			_screen->loadPalette(wsaData + 8 + ((_numFrames << 2) & 0xFFFF), *palBuf, 0x300);
65 	}
66 
67 	if (offscreenDecode) {
68 		_flags |= WF_OFFSCREEN_DECODE;
69 		const int offscreenBufferSize = _width * _height;
70 		_offscreenBuffer = new uint8[offscreenBufferSize];
71 		memset(_offscreenBuffer, 0, offscreenBufferSize);
72 	}
73 
74 	if (_numFrames & 0x8000) {
75 		// This is used in the Amiga version.
76 		if (_vm->gameFlags().platform != Common::kPlatformAmiga)
77 			warning("Unhandled wsa flags 0x8000");
78 		_flags |= WF_FLIPPED;
79 		_numFrames &= 0x7FFF;
80 	}
81 	_currentFrame = _numFrames;
82 
83 	_deltaBuffer = new uint8[_deltaBufferSize];
84 	memset(_deltaBuffer, 0, _deltaBufferSize);
85 
86 	// read frame offsets
87 	_frameOffsTable = new uint32[_numFrames + 2];
88 	_frameOffsTable[0] = 0;
89 	uint32 frameDataOffs = READ_LE_UINT32(wsaData); wsaData += 4;
90 	bool firstFrame = true;
91 
92 	if (frameDataOffs == 0) {
93 		firstFrame = false;
94 		frameDataOffs = READ_LE_UINT32(wsaData);
95 		_flags |= WF_NO_FIRST_FRAME;
96 	}
97 
98 	for (int i = 1; i < _numFrames + 2; ++i) {
99 		_frameOffsTable[i] = READ_LE_UINT32(wsaData);
100 		if (_frameOffsTable[i])
101 			_frameOffsTable[i] -= frameDataOffs;
102 		wsaData += 4;
103 	}
104 
105 	if (!_frameOffsTable[_numFrames + 1])
106 		_flags |= WF_NO_LAST_FRAME;
107 
108 	// skip palette
109 	wsaData += offsPal;
110 
111 	// read frame data
112 	const int frameDataSize = p + fileSize - wsaData;
113 	_frameData = new uint8[frameDataSize];
114 	memcpy(_frameData, wsaData, frameDataSize);
115 
116 	// decode first frame
117 	if (firstFrame)
118 		Screen::decodeFrame4(_frameData, _deltaBuffer, _deltaBufferSize);
119 
120 	delete[] p;
121 	_opened = true;
122 
123 	return _numFrames;
124 }
125 
close()126 void WSAMovie_v1::close() {
127 	if (_opened) {
128 		delete[] _deltaBuffer;
129 		delete[] _offscreenBuffer;
130 		delete[] _frameOffsTable;
131 		delete[] _frameData;
132 		_opened = false;
133 	}
134 }
135 
displayFrame(int frameNum,int pageNum,int x,int y,uint16 flags,const uint8 * table1,const uint8 * table2)136 void WSAMovie_v1::displayFrame(int frameNum, int pageNum, int x, int y, uint16 flags, const uint8 *table1, const uint8 *table2) {
137 	if (frameNum >= _numFrames || frameNum < 0 || !_opened)
138 		return;
139 
140 	_x = x;
141 	_y = y;
142 	_drawPage = pageNum;
143 
144 	uint8 *dst = 0;
145 	if (_flags & WF_OFFSCREEN_DECODE)
146 		dst = _offscreenBuffer;
147 	else
148 		dst = _screen->getPageRect(_drawPage, _x, _y, _width, _height);
149 
150 	if (_currentFrame == _numFrames) {
151 		if (!(_flags & WF_NO_FIRST_FRAME)) {
152 			if (_flags & WF_OFFSCREEN_DECODE)
153 				Screen::decodeFrameDelta(dst, _deltaBuffer);
154 			else
155 				Screen::decodeFrameDeltaPage(dst, _deltaBuffer, _width, (_flags & WF_XOR) == 0);
156 		}
157 		_currentFrame = 0;
158 	}
159 
160 	// try to reduce the number of needed frame operations
161 	int diffCount = ABS(_currentFrame - frameNum);
162 	int frameStep = 1;
163 	int frameCount;
164 	if (_currentFrame < frameNum) {
165 		frameCount = _numFrames - frameNum + _currentFrame;
166 		if (diffCount > frameCount && !(_flags & WF_NO_LAST_FRAME))
167 			frameStep = -1;
168 		else
169 			frameCount = diffCount;
170 	} else {
171 		frameCount = _numFrames - _currentFrame + frameNum;
172 		if (frameCount >= diffCount || (_flags & WF_NO_LAST_FRAME)) {
173 			frameStep = -1;
174 			frameCount = diffCount;
175 		}
176 	}
177 
178 	// process
179 	if (frameStep > 0) {
180 		uint16 cf = _currentFrame;
181 		while (frameCount--) {
182 			cf += frameStep;
183 			processFrame(cf, dst);
184 			if (cf == _numFrames)
185 				cf = 0;
186 		}
187 	} else {
188 		uint16 cf = _currentFrame;
189 		while (frameCount--) {
190 			if (cf == 0)
191 				cf = _numFrames;
192 			processFrame(cf, dst);
193 			cf += frameStep;
194 		}
195 	}
196 
197 	// display
198 	_currentFrame = frameNum;
199 	if (_flags & WF_OFFSCREEN_DECODE) {
200 		int pageBackUp = _screen->setCurPage(_drawPage);
201 
202 		int plotFunc = (flags & 0xFF00) >> 12;
203 		int unk1 = flags & 0xFF;
204 
205 		_screen->copyWsaRect(_x, _y, _width, _height, 0, plotFunc, _offscreenBuffer, unk1, table1, table2);
206 
207 		_screen->_curPage = pageBackUp;
208 	}
209 }
210 
processFrame(int frameNum,uint8 * dst)211 void WSAMovie_v1::processFrame(int frameNum, uint8 *dst) {
212 	if (!_opened)
213 		return;
214 	assert(frameNum <= _numFrames);
215 	const uint8 *src = _frameData + _frameOffsTable[frameNum];
216 	Screen::decodeFrame4(src, _deltaBuffer, _deltaBufferSize);
217 	if (_flags & WF_OFFSCREEN_DECODE)
218 		Screen::decodeFrameDelta(dst, _deltaBuffer);
219 	else
220 		Screen::decodeFrameDeltaPage(dst, _deltaBuffer, _width, false);
221 }
222 
223 #pragma mark -
224 
WSAMovieAmiga(KyraEngine_v1 * vm)225 WSAMovieAmiga::WSAMovieAmiga(KyraEngine_v1 *vm) : WSAMovie_v1(vm), _buffer(0) {}
226 
open(const char * filename,int offscreenDecode,Palette * palBuf)227 int WSAMovieAmiga::open(const char *filename, int offscreenDecode, Palette *palBuf) {
228 	int res = WSAMovie_v1::open(filename, offscreenDecode, palBuf);
229 
230 	if (!res)
231 		return 0;
232 
233 	_buffer = new uint8[_width * _height];
234 	assert(_buffer);
235 	return res;
236 }
237 
close()238 void WSAMovieAmiga::close() {
239 	if (_opened) {
240 		delete[] _buffer;
241 		_buffer = 0;
242 	}
243 	WSAMovie_v1::close();
244 }
245 
displayFrame(int frameNum,int pageNum,int x,int y,uint16 flags,const uint8 * table1,const uint8 * table2)246 void WSAMovieAmiga::displayFrame(int frameNum, int pageNum, int x, int y, uint16 flags, const uint8 *table1, const uint8 *table2) {
247 	if (frameNum >= _numFrames || frameNum < 0 || !_opened)
248 		return;
249 
250 	_x = x;
251 	_y = y;
252 	_drawPage = pageNum;
253 
254 	uint8 *dst;
255 	dst = _buffer;
256 	memset(_buffer, 0, _width * _height);
257 
258 	if (_currentFrame == _numFrames) {
259 		if (!(_flags & WF_NO_FIRST_FRAME)) {
260 			Screen::decodeFrameDelta(dst, _deltaBuffer, true);
261 			Screen::convertAmigaGfx(dst, _width, _height, 5, (_flags & WF_FLIPPED) != 0);
262 
263 			if (_flags & WF_OFFSCREEN_DECODE) {
264 				dst = _offscreenBuffer;
265 				const uint8 *src = _buffer;
266 				int size = _width * _height;
267 
268 				for (int i = 0; i < size; ++i)
269 					*dst++ ^= *src++;
270 
271 				dst = _buffer;
272 			} else {
273 				_screen->copyBlockToPage(_drawPage, _x, _y, _width, _height, _buffer);
274 			}
275 		}
276 		_currentFrame = 0;
277 	}
278 
279 	// try to reduce the number of needed frame operations
280 	int diffCount = ABS(_currentFrame - frameNum);
281 	int frameStep = 1;
282 	int frameCount;
283 	if (_currentFrame < frameNum) {
284 		frameCount = _numFrames - frameNum + _currentFrame;
285 		if (diffCount > frameCount && !(_flags & WF_NO_LAST_FRAME))
286 			frameStep = -1;
287 		else
288 			frameCount = diffCount;
289 	} else {
290 		frameCount = _numFrames - _currentFrame + frameNum;
291 		if (frameCount >= diffCount || (_flags & WF_NO_LAST_FRAME)) {
292 			frameStep = -1;
293 			frameCount = diffCount;
294 		}
295 	}
296 
297 	// process
298 	if (frameStep > 0) {
299 		uint16 cf = _currentFrame;
300 		while (frameCount--) {
301 			cf += frameStep;
302 			processFrame(cf, dst);
303 			if (cf == _numFrames)
304 				cf = 0;
305 		}
306 	} else {
307 		uint16 cf = _currentFrame;
308 		while (frameCount--) {
309 			if (cf == 0)
310 				cf = _numFrames;
311 			processFrame(cf, dst);
312 			cf += frameStep;
313 		}
314 	}
315 
316 	// display
317 	_currentFrame = frameNum;
318 	if (_flags & WF_OFFSCREEN_DECODE) {
319 		int pageBackUp = _screen->setCurPage(_drawPage);
320 
321 		int plotFunc = (flags & 0xFF00) >> 12;
322 		int unk1 = flags & 0xFF;
323 
324 		_screen->copyWsaRect(_x, _y, _width, _height, 0, plotFunc, _offscreenBuffer, unk1, table1, table2);
325 
326 		_screen->_curPage = pageBackUp;
327 	}
328 }
329 
processFrame(int frameNum,uint8 * dst)330 void WSAMovieAmiga::processFrame(int frameNum, uint8 *dst) {
331 	if (!_opened)
332 		return;
333 	assert(frameNum <= _numFrames);
334 
335 	memset(dst, 0, _width * _height);
336 
337 	const uint8 *src = _frameData + _frameOffsTable[frameNum];
338 	Screen::decodeFrame4(src, _deltaBuffer, _deltaBufferSize);
339 	Screen::decodeFrameDelta(dst, _deltaBuffer, true);
340 	Screen::convertAmigaGfx(dst, _width, _height, 5, (_flags & WF_FLIPPED) != 0);
341 
342 	src = dst;
343 	dst = 0;
344 	int dstPitch = 0;
345 	if (_flags & WF_OFFSCREEN_DECODE) {
346 		dst = _offscreenBuffer;
347 		dstPitch = _width;
348 	} else {
349 		dst = _screen->getPageRect(_drawPage, _x, _y, _width, _height);
350 		dstPitch = Screen::SCREEN_W;
351 	}
352 
353 	for (int y = 0; y < _height; ++y) {
354 		for (int x = 0; x < _width; ++x)
355 			*dst++ ^= *src++;
356 		dst += dstPitch - _width;
357 	}
358 }
359 
360 #pragma mark -
361 
WSAMovie_v2(KyraEngine_v1 * vm)362 WSAMovie_v2::WSAMovie_v2(KyraEngine_v1 *vm) : WSAMovie_v1(vm), _xAdd(0), _yAdd(0) {}
363 
open(const char * filename,int unk1,Palette * palBuf)364 int WSAMovie_v2::open(const char *filename, int unk1, Palette *palBuf) {
365 	close();
366 
367 	uint32 flags = 0;
368 	uint32 fileSize;
369 	uint8 *p = _vm->resource()->fileData(filename, &fileSize);
370 	if (!p) {
371 		warning("couldn't load wsa file: '%s'", filename);
372 		return 0;
373 	}
374 
375 	const uint8 *wsaData = p;
376 	_numFrames = READ_LE_UINT16(wsaData); wsaData += 2;
377 	_xAdd = (int16)(READ_LE_UINT16(wsaData)); wsaData += 2;
378 	_yAdd = (int16)(READ_LE_UINT16(wsaData)); wsaData += 2;
379 	_width = READ_LE_UINT16(wsaData); wsaData += 2;
380 	_height = READ_LE_UINT16(wsaData); wsaData += 2;
381 	_deltaBufferSize = READ_LE_UINT16(wsaData); wsaData += 2;
382 	_offscreenBuffer = NULL;
383 	_flags = 0;
384 	flags = READ_LE_UINT16(wsaData); wsaData += 2;
385 
386 	uint32 offsPal = 0;
387 	if (flags & 1) {
388 		offsPal = 0x300;
389 		_flags |= WF_HAS_PALETTE;
390 		if (palBuf)
391 			_screen->loadPalette(wsaData + 8 + ((_numFrames << 2) & 0xFFFF), *palBuf, 0x300);
392 	}
393 
394 	if (flags & 2) {
395 		if (_vm->gameFlags().use16ColorMode) {
396 			offsPal = 0x30;
397 			_flags |= WF_HAS_PALETTE;
398 			if (palBuf)
399 				_screen->loadPalette(wsaData + 8 + ((_numFrames << 2) & 0xFFFF), *palBuf, 0x30);
400 		}
401 
402 		_flags |= WF_XOR;
403 	}
404 
405 
406 	if (!(unk1 & 2)) {
407 		_flags |= WF_OFFSCREEN_DECODE;
408 		const int offscreenBufferSize = _width * _height;
409 		_offscreenBuffer = new uint8[offscreenBufferSize];
410 		memset(_offscreenBuffer, 0, offscreenBufferSize);
411 	}
412 
413 	if (_numFrames & 0x8000) {
414 		warning("Unhandled wsa flags 0x80");
415 		_flags |= 0x80;
416 		_numFrames &= 0x7FFF;
417 	}
418 	_currentFrame = _numFrames;
419 
420 	_deltaBuffer = new uint8[_deltaBufferSize];
421 	memset(_deltaBuffer, 0, _deltaBufferSize);
422 
423 	// read frame offsets
424 	_frameOffsTable = new uint32[_numFrames + 2];
425 	_frameOffsTable[0] = 0;
426 	uint32 frameDataOffs = READ_LE_UINT32(wsaData); wsaData += 4;
427 	bool firstFrame = true;
428 	if (frameDataOffs == 0) {
429 		firstFrame = false;
430 		frameDataOffs = READ_LE_UINT32(wsaData);
431 		_flags |= WF_NO_FIRST_FRAME;
432 	}
433 
434 	for (int i = 1; i < _numFrames + 2; ++i) {
435 		_frameOffsTable[i] = READ_LE_UINT32(wsaData);
436 		if (_frameOffsTable[i])
437 			_frameOffsTable[i] -= frameDataOffs;
438 		wsaData += 4;
439 	}
440 
441 	if (!_frameOffsTable[_numFrames + 1])
442 		_flags |= WF_NO_LAST_FRAME;
443 
444 	// skip palette
445 	wsaData += offsPal;
446 
447 	// read frame data
448 	const int frameDataSize = p + fileSize - wsaData;
449 
450 	_frameData = new uint8[frameDataSize];
451 	memcpy(_frameData, wsaData, frameDataSize);
452 
453 	// decode first frame
454 	if (firstFrame)
455 		Screen::decodeFrame4(_frameData, _deltaBuffer, _deltaBufferSize);
456 
457 	delete[] p;
458 	_opened = true;
459 
460 	return _numFrames;
461 }
462 
463 } // End of namespace Kyra
464