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