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