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 * Additional copyright for this file:
8 * Copyright (C) 1994-1998 Revolution Software Ltd.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 */
24
25 #include "common/file.h"
26 #include "common/endian.h"
27
28 #include "sword2/sword2.h"
29 #include "sword2/header.h"
30 #include "sword2/resman.h"
31 #include "sword2/logic.h"
32
33 namespace Sword2 {
34
35 /**
36 * Returns a pointer to the first palette entry, given the pointer to the start
37 * of the screen file.
38 */
39
fetchPalette(byte * screenFile,byte * palBuffer)40 void Sword2Engine::fetchPalette(byte *screenFile, byte *palBuffer) {
41 byte *palette;
42
43 if (isPsx()) { // PSX version doesn't have a "MultiScreenHeader", instead there's a ScreenHeader and a tag
44 palette = screenFile + ResHeader::size() + ScreenHeader::size() + 2;
45 } else {
46 MultiScreenHeader mscreenHeader;
47
48 mscreenHeader.read(screenFile + ResHeader::size());
49 palette = screenFile + ResHeader::size() + mscreenHeader.palette;
50 }
51
52 // Always set color 0 to black, because while most background screen
53 // palettes have a bright color 0 it should come out as black in the
54 // game.
55
56 palBuffer[0] = 0;
57 palBuffer[1] = 0;
58 palBuffer[2] = 0;
59
60 for (uint i = 4, j = 3; i < 4 * 256; i += 4, j += 3) {
61 palBuffer[j + 0] = palette[i + 0];
62 palBuffer[j + 1] = palette[i + 1];
63 palBuffer[j + 2] = palette[i + 2];
64 }
65 }
66
67 /**
68 * Returns a pointer to the start of the palette match table, given the pointer
69 * to the start of the screen file.
70 * It returns NULL when used with PSX version, as there are no palette match tables in
71 * the resource files.
72 */
73
fetchPaletteMatchTable(byte * screenFile)74 byte *Sword2Engine::fetchPaletteMatchTable(byte *screenFile) {
75
76 if (isPsx()) return NULL;
77
78 MultiScreenHeader mscreenHeader;
79
80 mscreenHeader.read(screenFile + ResHeader::size());
81
82 return screenFile + ResHeader::size() + mscreenHeader.paletteTable;
83 }
84
85 /**
86 * Returns a pointer to the screen header, given the pointer to the start of
87 * the screen file.
88 */
89
fetchScreenHeader(byte * screenFile)90 byte *Sword2Engine::fetchScreenHeader(byte *screenFile) {
91 if (isPsx()) { // In PSX version there's no MultiScreenHeader, so just skip resource header
92 return screenFile + ResHeader::size();
93 } else {
94 MultiScreenHeader mscreenHeader;
95
96 mscreenHeader.read(screenFile + ResHeader::size());
97 return screenFile + ResHeader::size() + mscreenHeader.screen;
98 }
99 }
100
101 /**
102 * Returns a pointer to the requested layer header, given the pointer to the
103 * start of the screen file. Drops out if the requested layer number exceeds
104 * the number of layers on this screen.
105 */
106
fetchLayerHeader(byte * screenFile,uint16 layerNo)107 byte *Sword2Engine::fetchLayerHeader(byte *screenFile, uint16 layerNo) {
108 #ifdef SWORD2_DEBUG
109 ScreenHeader screenHead;
110
111 screenHead.read(fetchScreenHeader(screenFile));
112 assert(layerNo < screenHead.noLayers);
113 #endif
114
115 if (isPsx()) {
116 return screenFile + ResHeader::size() + ScreenHeader::size() + 2 + 0x400 + layerNo * LayerHeader::size();
117 } else {
118 MultiScreenHeader mscreenHeader;
119
120 mscreenHeader.read(screenFile + ResHeader::size());
121 return screenFile + ResHeader::size() + mscreenHeader.layers + layerNo * LayerHeader::size();
122 }
123 }
124
125 /**
126 * Returns a pointer to the start of the shading mask, given the pointer to the
127 * start of the screen file.
128 * If we are non PSX, this will return NULL, as we don't have shading masks.
129 */
130
fetchShadingMask(byte * screenFile)131 byte *Sword2Engine::fetchShadingMask(byte *screenFile) {
132 if (isPsx()) return NULL;
133
134 MultiScreenHeader mscreenHeader;
135
136 mscreenHeader.read(screenFile + ResHeader::size());
137
138 return screenFile + ResHeader::size() + mscreenHeader.maskOffset;
139 }
140
141 /**
142 * Returns a pointer to the anim header, given the pointer to the start of the
143 * anim file.
144 */
145
fetchAnimHeader(byte * animFile)146 byte *Sword2Engine::fetchAnimHeader(byte *animFile) {
147 return animFile + ResHeader::size();
148 }
149
150 /**
151 * Returns a pointer to the requested frame number's cdtEntry, given the
152 * pointer to the start of the anim file. Drops out if the requested frame
153 * number exceeds the number of frames in this anim.
154 */
155
fetchCdtEntry(byte * animFile,uint16 frameNo)156 byte *Sword2Engine::fetchCdtEntry(byte *animFile, uint16 frameNo) {
157 #ifdef SWORD2_DEBUG
158 AnimHeader animHead;
159
160 animHead.read(fetchAnimHeader(animFile));
161
162 if (frameNo > animHead->noAnimFrames - 1)
163 error("fetchCdtEntry(animFile,%d) - anim only %d frames", frameNo, animHead->noAnimFrames);
164 #endif
165
166 return fetchAnimHeader(animFile) + AnimHeader::size() + frameNo * CdtEntry::size();
167 }
168
169 /**
170 * Returns a pointer to the requested frame number's header, given the pointer
171 * to the start of the anim file. Drops out if the requested frame number
172 * exceeds the number of frames in this anim
173 */
174
fetchFrameHeader(byte * animFile,uint16 frameNo)175 byte *Sword2Engine::fetchFrameHeader(byte *animFile, uint16 frameNo) {
176 // required address = (address of the start of the anim header) + frameOffset
177
178 CdtEntry cdt;
179
180 cdt.read(fetchCdtEntry(animFile, frameNo));
181
182 return animFile + ResHeader::size() + cdt.frameOffset;
183 }
184
185 /**
186 * Returns a pointer to the requested parallax layer data.
187 */
188
fetchBackgroundParallaxLayer(byte * screenFile,int layer)189 byte *Sword2Engine::fetchBackgroundParallaxLayer(byte *screenFile, int layer) {
190 if (isPsx()) {
191 byte *psxParallax = _screen->getPsxScrCache(0);
192
193 // Manage cache for background psx parallaxes
194 if (!_screen->getPsxScrCacheStatus(0)) { // This parallax layer is not present
195 return NULL;
196 } else if (psxParallax != NULL) { // Parallax layer present, and already in cache
197 return psxParallax;
198 } else { // Present, but not cached
199 uint32 locNo = _logic->getLocationNum();
200
201 // At game startup, we have a wrong location number stored
202 // in game vars (0, instead of 3), work around this.
203 locNo = (locNo == 0) ? 3 : locNo;
204
205 psxParallax = fetchPsxParallax(locNo, 0);
206 _screen->setPsxScrCache(psxParallax, 0);
207 return psxParallax;
208 }
209 } else {
210 MultiScreenHeader mscreenHeader;
211
212 mscreenHeader.read(screenFile + ResHeader::size());
213 assert(mscreenHeader.bg_parallax[layer]);
214 return screenFile + ResHeader::size() + mscreenHeader.bg_parallax[layer];
215 }
216 }
217
fetchBackgroundLayer(byte * screenFile)218 byte *Sword2Engine::fetchBackgroundLayer(byte *screenFile) {
219 if (isPsx()) {
220 byte *psxBackground = _screen->getPsxScrCache(1);
221
222 // Manage cache for psx backgrounds
223 if (psxBackground) { // Background is cached
224 return psxBackground;
225 } else { // Background not cached
226 uint32 locNo = _logic->getLocationNum();
227
228 // We have a wrong location number at start, fix that
229 locNo = (locNo == 0) ? 3 : locNo;
230
231 psxBackground = fetchPsxBackground(locNo);
232 _screen->setPsxScrCache(psxBackground, 1);
233 return psxBackground;
234 }
235 } else {
236 MultiScreenHeader mscreenHeader;
237
238 mscreenHeader.read(screenFile + ResHeader::size());
239 assert(mscreenHeader.screen);
240 return screenFile + ResHeader::size() + mscreenHeader.screen + ScreenHeader::size();
241 }
242 }
243
fetchForegroundParallaxLayer(byte * screenFile,int layer)244 byte *Sword2Engine::fetchForegroundParallaxLayer(byte *screenFile, int layer) {
245 if (isPsx()) {
246 byte *psxParallax = _screen->getPsxScrCache(2);
247
248 // Manage cache for psx parallaxes
249 if (!_screen->getPsxScrCacheStatus(2)) { // This parallax layer is not present
250 return NULL;
251 } else if (psxParallax) { // Parallax layer present and cached
252 return psxParallax;
253 } else { // Present, but still not cached
254 uint32 locNo = _logic->getLocationNum();
255
256 // We have a wrong location number at start, fix that
257 locNo = (locNo == 0) ? 3 : locNo;
258
259 psxParallax = fetchPsxParallax(locNo, 1);
260 _screen->setPsxScrCache(psxParallax, 2);
261 return psxParallax;
262 }
263 } else {
264 MultiScreenHeader mscreenHeader;
265
266 mscreenHeader.read(screenFile + ResHeader::size());
267 assert(mscreenHeader.fg_parallax[layer]);
268 return screenFile + ResHeader::size() + mscreenHeader.fg_parallax[layer];
269 }
270 }
271
fetchTextLine(byte * file,uint32 text_line)272 byte *Sword2Engine::fetchTextLine(byte *file, uint32 text_line) {
273 TextHeader text_header;
274 static byte errorLine[128];
275
276 text_header.read(file + ResHeader::size());
277
278 if (text_line >= text_header.noOfLines) {
279 sprintf((char *)errorLine, "xxMissing line %d of %s (only 0..%d)", text_line, _resman->fetchName(file), text_header.noOfLines - 1);
280
281 // first 2 chars are NULL so that actor-number comes out as '0'
282 errorLine[0] = 0;
283 errorLine[1] = 0;
284 return errorLine;
285 }
286
287 // The "number of lines" field is followed by a lookup table
288
289 return file + READ_LE_UINT32(file + ResHeader::size() + 4 + 4 * text_line);
290 }
291
292 /**
293 * Returns a pointer to psx background data for passed location number
294 * At the beginning of the passed data there's an artificial header composed by
295 * uint16: background X resolution
296 * uint16: background Y resolution
297 * uint32: offset to subtract from offset table entries
298 */
299
fetchPsxBackground(uint32 location)300 byte *Sword2Engine::fetchPsxBackground(uint32 location) {
301 Common::File file;
302 PSXScreensEntry header;
303 uint32 screenOffset, dataOffset;
304 uint32 totSize; // Total size of background, counting data, offset table and additional header
305 byte *buffer;
306
307 if (!file.open("screens.clu")) {
308 GUIErrorMessage("Broken Sword II: Cannot open screens.clu");
309 return NULL;
310 }
311
312 file.seek(location * 4, SEEK_SET);
313 screenOffset = file.readUint32LE();
314
315 if (screenOffset == 0) { // We don't have screen data for this location number.
316 file.close();
317 return NULL;
318 }
319
320 // Get to the beginning of PSXScreensEntry
321 file.seek(screenOffset + ResHeader::size(), SEEK_SET);
322
323 buffer = (byte *)malloc(PSXScreensEntry::size());
324 file.read(buffer, PSXScreensEntry::size());
325
326 // Prepare the header
327 header.read(buffer);
328 free(buffer);
329
330 file.seek(screenOffset + header.bgOffset + 4, SEEK_SET);
331 dataOffset = file.readUint32LE();
332
333 file.seek(screenOffset + header.bgOffset, SEEK_SET);
334
335 totSize = header.bgSize + (dataOffset - header.bgOffset) + 8;
336 buffer = (byte *)malloc(totSize);
337
338 // Write some informations before background data
339 WRITE_LE_UINT16(buffer, header.bgXres);
340 WRITE_LE_UINT16(buffer + 2, header.bgYres);
341 WRITE_LE_UINT32(buffer + 4, header.bgOffset);
342
343 file.read(buffer + 8, totSize - 8); // Do not write on the header
344 file.close();
345
346 return buffer;
347 }
348
349 /**
350 * Returns a pointer to selected psx parallax data for passed location number
351 * At the beginning of the passed data there's an artificial header composed by
352 * uint16: parallax X resolution
353 * uint16: parallax Y resolution
354 * uint16: width in 64x16 tiles of parallax
355 * uint16: height in 64x16 tiles of parallax
356 */
357
fetchPsxParallax(uint32 location,uint8 level)358 byte *Sword2Engine::fetchPsxParallax(uint32 location, uint8 level) {
359 Common::File file;
360 PSXScreensEntry header;
361 uint32 screenOffset;
362 uint16 horTiles; // Number of horizontal tiles in the parallax grid
363 uint16 verTiles; // Number of vertical tiles in parallax grid
364 uint32 totSize; // Total size of parallax, counting data, grid, and additional header
365 byte *buffer;
366
367 uint16 plxXres;
368 uint16 plxYres;
369 uint32 plxOffset;
370 uint32 plxSize;
371
372 if (level > 1)
373 return NULL;
374
375 if (!file.open("screens.clu")) {
376 GUIErrorMessage("Broken Sword II: Cannot open screens.clu");
377 return NULL;
378 }
379
380 file.seek(location * 4, SEEK_SET);
381 screenOffset = file.readUint32LE();
382
383 if (screenOffset == 0) // There is no screen here
384 return NULL;
385
386 // Get to the beginning of PSXScreensEntry
387 file.seek(screenOffset + ResHeader::size(), SEEK_SET);
388
389 buffer = (byte *)malloc(PSXScreensEntry::size());
390 file.read(buffer, PSXScreensEntry::size());
391
392 // Initialize the header
393 header.read(buffer);
394 free(buffer);
395
396 // We are fetching...
397 if (level == 0) { // a background parallax
398 plxXres = header.bgPlxXres;
399 plxYres = header.bgPlxYres;
400 plxOffset = header.bgPlxOffset;
401 plxSize = header.bgPlxSize;
402 } else { // a foreground parallax
403 plxXres = header.fgPlxXres;
404 plxYres = header.fgPlxYres;
405 plxOffset = header.fgPlxOffset;
406 plxSize = header.fgPlxSize;
407 }
408
409 if (plxXres == 0 || plxYres == 0 || plxSize == 0) // This screen has no parallax data.
410 return NULL;
411
412 debug(2, "fetchPsxParallax() -> %s parallax, xRes: %u, yRes: %u", (level == 0) ? "Background" : "Foreground", plxXres, plxYres);
413
414 // Calculate the number of tiles which compose the parallax grid.
415 horTiles = (plxXres % 64) ? (plxXres / 64) + 1 : plxXres / 64;
416 verTiles = (plxYres % 16) ? (plxYres / 16) + 1 : plxYres / 16;
417
418 totSize = plxSize + horTiles * verTiles * 4 + 8;
419
420 file.seek(screenOffset + plxOffset, SEEK_SET);
421 buffer = (byte *)malloc(totSize);
422
423 // Insert parallax resolution information in the buffer,
424 // preceding parallax data.
425 WRITE_LE_UINT16(buffer, plxXres);
426 WRITE_LE_UINT16(buffer + 2, plxYres);
427 WRITE_LE_UINT16(buffer + 4, horTiles);
428 WRITE_LE_UINT16(buffer + 6, verTiles);
429
430 // Read parallax data from file and store it inside the buffer,
431 // skipping the generated header.
432 file.read(buffer + 8, totSize - 8);
433 file.close();
434
435 return buffer;
436 }
437
438 // Used for testing text & speech (see fnISpeak in speech.cpp)
439
checkTextLine(byte * file,uint32 text_line)440 bool Sword2Engine::checkTextLine(byte *file, uint32 text_line) {
441 TextHeader text_header;
442
443 text_header.read(file + ResHeader::size());
444
445 return text_line < text_header.noOfLines;
446 }
447
448 } // End of namespace Sword2
449