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 "common/scummsys.h"
24 #include "common/endian.h"
25 #include "common/file.h"
26 #include "common/stack.h"
27 #include "common/util.h"
28 #include "tsage/resources.h"
29 #include "tsage/tsage.h"
30 
31 namespace TsAGE {
32 
33 
MemoryManager()34 MemoryManager::MemoryManager() {
35 	_memoryPool = new MemoryHeader*[MEMORY_POOL_SIZE];
36 	Common::fill(&_memoryPool[0], &_memoryPool[MEMORY_POOL_SIZE], (MemoryHeader *)NULL);
37 }
38 
~MemoryManager()39 MemoryManager::~MemoryManager() {
40 	for (int i = 0; i < MEMORY_POOL_SIZE; ++i) {
41 		if (_memoryPool[i] != NULL)
42 			free(_memoryPool[i]);
43 	}
44 	delete[] _memoryPool;
45 }
46 
allocate(uint32 size)47 uint16 MemoryManager::allocate(uint32 size) {
48 	int idx = 0;
49 	while ((idx < MEMORY_POOL_SIZE) && (_memoryPool[idx] != NULL))
50 		++idx;
51 	if (idx == MEMORY_POOL_SIZE)
52 		error("Out of memory handles");
53 
54 	// Create the new entry
55 	_memoryPool[idx] = (MemoryHeader *)malloc(sizeof(MemoryHeader) + size);
56 	_memoryPool[idx]->id = MEMORY_ENTRY_ID;
57 	_memoryPool[idx]->index = idx;
58 	_memoryPool[idx]->lockCtr = 0;
59 	_memoryPool[idx]->criticalCtr = 0;
60 	_memoryPool[idx]->tag = 0;
61 	_memoryPool[idx]->size = size;
62 
63 	// Return it's index
64 	return idx;
65 }
66 
allocate2(uint32 size)67 byte *MemoryManager::allocate2(uint32 size) {
68 	uint32 idx = allocate(size);
69 	byte *result = lock(idx);
70 	Common::fill(result, result + size, 0);
71 	return result;
72 }
73 
lock(uint32 handle)74 byte *MemoryManager::lock(uint32 handle) {
75 	assert((int)handle < MEMORY_POOL_SIZE);
76 	return (byte *)_memoryPool[handle] + sizeof(MemoryHeader);
77 }
78 
indexOf(const byte * p)79 int MemoryManager::indexOf(const byte *p) {
80 	for (int idx = 0; idx < MEMORY_POOL_SIZE; ++idx) {
81 		if (((byte *)_memoryPool[idx] + sizeof(MemoryHeader)) == p)
82 			return idx;
83 	}
84 
85 	return -1;
86 }
87 
deallocate(const byte * p)88 void MemoryManager::deallocate(const byte *p) {
89 	if (!p)
90 		return;
91 
92 	int idx = indexOf(p);
93 	assert(idx != -1);
94 	if (_memoryPool[idx]->lockCtr-- == 0) {
95 		free(_memoryPool[idx]);
96 		_memoryPool[idx] = NULL;
97 	}
98 }
99 
getSize(const byte * p)100 uint32 MemoryManager::getSize(const byte *p) {
101 	int idx = indexOf(p);
102 	assert(idx >= 0);
103 	return _memoryPool[idx]->size;
104 }
105 
incLocks(const byte * p)106 void MemoryManager::incLocks(const byte *p) {
107 	int idx = indexOf(p);
108 	assert(idx >= 0);
109 	_memoryPool[idx]->lockCtr++;
110 }
111 
112 /*-------------------------------------------------------------------------*/
113 
114 static uint16 bitMasks[4] = {0x1ff, 0x3ff, 0x7ff, 0xfff};
115 
readToken()116 uint16 BitReader::readToken() {
117 	assert((numBits >= 9) && (numBits <= 12));
118 	uint16 result = _remainder;
119 	int bitsLeft = numBits - _bitsLeft;
120 	int bitOffset = _bitsLeft;
121 	_bitsLeft = 0;
122 
123 	while (bitsLeft >= 0) {
124 		_remainder = readByte();
125 		result |= _remainder << bitOffset;
126 		bitsLeft -= 8;
127 		bitOffset += 8;
128 	}
129 
130 	_bitsLeft = -bitsLeft;
131 	_remainder >>= 8 - _bitsLeft;
132 	return result & bitMasks[numBits - 9];
133 }
134 
135 /*-------------------------------------------------------------------------*/
136 
TLib(MemoryManager & memManager,const Common::String & filename)137 TLib::TLib(MemoryManager &memManager, const Common::String &filename) :
138 		_filename(filename), _memoryManager(memManager) {
139 
140 	// If the resource strings list isn't yet loaded, load them
141 	if (_resStrings.size() == 0) {
142 		Common::File f;
143 		if (f.open("tsage.cfg")) {
144 			while (!f.eos()) {
145 				_resStrings.push_back(f.readLine());
146 			}
147 			f.close();
148 		}
149 	}
150 
151 	if (!_file.open(filename))
152 		error("Missing file %s", filename.c_str());
153 
154 	loadIndex();
155 }
156 
~TLib()157 TLib::~TLib() {
158 	_resStrings.clear();
159 }
160 
161 /**
162  * Load a section index from the given position in the file
163  */
loadSection(uint32 fileOffset)164 void TLib::loadSection(uint32 fileOffset) {
165 	_resources.clear();
166 	_file.seek(fileOffset);
167 	_sections.fileOffset = fileOffset;
168 
169 	ResourceManager::loadSection(_file, _resources);
170 }
171 
172 struct DecodeReference {
173 	uint16 vWord;
174 	uint8 vByte;
175 };
176 
177 /**
178  * Gets a resource from the currently loaded section
179  */
getResource(uint16 id,bool suppressErrors)180 byte *TLib::getResource(uint16 id, bool suppressErrors) {
181 	// Scan for an entry for the given Id
182 	ResourceEntry *re = NULL;
183 	ResourceList::iterator iter;
184 	for (iter = _resources.begin(); iter != _resources.end(); ++iter) {
185 		if ((*iter).id == id) {
186 			re = &(*iter);
187 			break;
188 		}
189 	}
190 	if (!re) {
191 		if (suppressErrors)
192 			return NULL;
193 		error("Could not find resource Id #%d", id);
194 	}
195 
196 	if (!re->isCompressed) {
197 		// Read in the resource data and return it
198 		byte *dataP = _memoryManager.allocate2(re->size);
199 		_file.seek(_sections.fileOffset + re->fileOffset);
200 		_file.read(dataP, re->size);
201 
202 		return dataP;
203 	}
204 
205 	/*
206 	 * Decompress the data block
207 	 */
208 
209 	_file.seek(_sections.fileOffset + re->fileOffset);
210 	Common::ReadStream *compStream = _file.readStream(re->size);
211 	BitReader bitReader(*compStream);
212 
213 	byte *dataOut = _memoryManager.allocate2(re->uncompressedSize);
214 	byte *destP = dataOut;
215 	uint bytesWritten = 0;
216 
217 	uint16 ctrCurrent = 0x102, ctrMax = 0x200;
218 	uint16 word_48050 = 0, currentToken = 0, word_48054 =0;
219 	byte byte_49068 = 0, byte_49069 = 0;
220 
221 	const uint tableSize = 0x1000;
222 	DecodeReference *table = (DecodeReference *)malloc(tableSize * sizeof(DecodeReference));
223 	if (!table)
224 		error("[TLib::getResource] Cannot allocate table buffer");
225 
226 	for (uint i = 0; i < tableSize; ++i) {
227 		table[i].vByte = table[i].vWord = 0;
228 	}
229 	Common::Stack<uint16> tokenList;
230 
231 	for (;;) {
232 		// Get the next decode token
233 		uint16 token = bitReader.readToken();
234 
235 		// Handle the token
236 		if (token == 0x101) {
237 			// End of compressed stream
238 			break;
239 		} else if (token == 0x100) {
240 			// Reset bit-rate
241 			bitReader.numBits = 9;
242 			ctrMax = 0x200;
243 			ctrCurrent = 0x102;
244 
245 			// Set variables with next token
246 			currentToken = word_48050 = bitReader.readToken();
247 			byte_49069 = byte_49068 = (byte)currentToken;
248 
249 			++bytesWritten;
250 			assert(bytesWritten <= re->uncompressedSize);
251 			*destP++ = byte_49069;
252 		} else {
253 			word_48054 = word_48050 = token;
254 
255 			if (token >= ctrCurrent) {
256 				word_48050 = currentToken;
257 				tokenList.push(byte_49068);
258 			}
259 
260 			while (word_48050 >= 0x100) {
261 				assert(word_48050 < 0x1000);
262 				tokenList.push(table[word_48050].vByte);
263 				word_48050 = table[word_48050].vWord;
264 			}
265 
266 			byte_49069 = byte_49068 = (byte)word_48050;
267 			tokenList.push(word_48050);
268 
269 			// Write out any cached tokens
270 			while (!tokenList.empty()) {
271 				++bytesWritten;
272 				assert(bytesWritten <= re->uncompressedSize);
273 				*destP++ = tokenList.pop();
274 			}
275 
276 			assert(ctrCurrent < 0x1000);
277 			table[ctrCurrent].vByte = byte_49069;
278 			table[ctrCurrent].vWord = currentToken;
279 			++ctrCurrent;
280 
281 			currentToken = word_48054;
282 			if ((ctrCurrent >= ctrMax) && (bitReader.numBits != 12)) {
283 				// Move to the next higher bit-rate
284 				++bitReader.numBits;
285 				ctrMax <<= 1;
286 			}
287 		}
288 	}
289 
290 	free(table);
291 
292 	assert(bytesWritten == re->uncompressedSize);
293 	delete compStream;
294 	return dataOut;
295 }
296 
297 /**
298  * Finds the correct section and loads the specified resource within it
299  */
getResource(ResourceType resType,uint16 resNum,uint16 rlbNum,bool suppressErrors)300 byte *TLib::getResource(ResourceType resType, uint16 resNum, uint16 rlbNum, bool suppressErrors) {
301 	SectionList::iterator i = _sections.begin();
302 	while ((i != _sections.end()) && ((*i).resType != resType || (*i).resNum != resNum))
303 		++i;
304 	if (i == _sections.end()) {
305 		if (suppressErrors)
306 			return NULL;
307 		error("Unknown resource type %d num %d", resType, resNum);
308 	}
309 
310 	loadSection((*i).fileOffset);
311 
312 	return getResource(rlbNum, suppressErrors);
313 }
314 
315 /**
316  * Gets the offset of the start of a resource in the resource file
317  */
getResourceStart(ResourceType resType,uint16 resNum,uint16 rlbNum,ResourceEntry & entry)318 uint32 TLib::getResourceStart(ResourceType resType, uint16 resNum, uint16 rlbNum, ResourceEntry &entry) {
319 	// Find the correct section
320 	SectionList::iterator i = _sections.begin();
321 	while ((i != _sections.end()) && ((*i).resType != resType || (*i).resNum != resNum))
322 		++i;
323 	if (i == _sections.end()) {
324 		error("Unknown resource type %d num %d", resType, resNum);
325 	}
326 
327 	// Load in the section index
328 	loadSection((*i).fileOffset);
329 
330 	// Scan for an entry for the given Id
331 	ResourceEntry *re = NULL;
332 	ResourceList::iterator iter;
333 	for (iter = _resources.begin(); iter != _resources.end(); ++iter) {
334 		if ((*iter).id == rlbNum) {
335 			re = &(*iter);
336 			break;
337 		}
338 	}
339 
340 	// Throw an error if no resource was found, or the resource is compressed
341 	if (!re || re->isCompressed)
342 		error("Invalid resource Id #%d", rlbNum);
343 
344 	// Return the resource entry as well as the file offset
345 	entry = *re;
346 	return _sections.fileOffset + entry.fileOffset;
347 }
348 
loadIndex()349 void TLib::loadIndex() {
350 	uint16 resNum, configId, fileOffset;
351 
352 	// Load the root resources section
353 	loadSection(0);
354 
355 	// Get the single resource from it
356 	const byte *pData = getResource(0);
357 	const byte *p = pData;
358 
359 	_sections.clear();
360 
361 	// Loop through reading the entries
362 	while ((resNum = READ_LE_UINT16(p)) != 0xffff) {
363 		configId = READ_LE_UINT16(p + 2);
364 		fileOffset = READ_LE_UINT16(p + 4);
365 		p += 6;
366 
367 		SectionEntry se;
368 		se.resNum = resNum;
369 		se.resType = (ResourceType)(configId & 0x1f);
370 		se.fileOffset = (((configId >> 5) & 0x7ff) << 16) | fileOffset;
371 		if (g_vm->getGameID() == GType_Ringworld2)
372 			se.fileOffset <<= 4;
373 
374 		_sections.push_back(se);
375 	}
376 
377 	_memoryManager.deallocate(pData);
378 }
379 
380 /**
381  * Retrieves the specified palette resource and returns it's data
382  *
383  * @paletteNum Specefies the palette number
384  */
getPalette(int paletteNum,byte * palData,uint * startNum,uint * numEntries)385 bool TLib::getPalette(int paletteNum, byte *palData, uint *startNum, uint *numEntries) {
386 	// Get the specified palette
387 	byte *dataIn = getResource(RES_PALETTE, 0, paletteNum, true);
388 	if (!dataIn)
389 		return false;
390 
391 	*startNum = READ_LE_UINT16(dataIn);
392 	*numEntries = READ_LE_UINT16(dataIn + 2);
393 	assert((*startNum < 256) && ((*startNum + *numEntries) <= 256));
394 
395 	// Copy over the data
396 	Common::copy(&dataIn[6], &dataIn[6 + *numEntries * 3], palData);
397 
398 	_memoryManager.deallocate(dataIn);
399 	return true;
400 }
401 
getSubResource(int resNum,int rlbNum,int index,uint * size,bool suppressErrors)402 byte *TLib::getSubResource(int resNum, int rlbNum, int index, uint *size, bool suppressErrors) {
403 	// Get the specified image set
404 	byte *dataIn = getResource(RES_VISAGE, resNum, rlbNum);
405 	if (!dataIn) {
406 		if (suppressErrors)
407 			return NULL;
408 
409 		error("Unknown sub resource %d/%d index %d", resNum, rlbNum, index);
410 	}
411 
412 	int numEntries = READ_LE_UINT16(dataIn);
413 	uint32 entryOffset = READ_LE_UINT32(dataIn + 2 + (index - 1) * 4);
414 	uint32 nextOffset = (index == numEntries) ?
415 			_memoryManager.getSize(dataIn) : READ_LE_UINT32(dataIn + 2 + index * 4);
416 	*size = nextOffset - entryOffset;
417 	assert(*size < (1024 * 1024));
418 
419 	byte *entry = _memoryManager.allocate2(*size);
420 	Common::copy(&dataIn[entryOffset], &dataIn[nextOffset], entry);
421 
422 	_memoryManager.deallocate(dataIn);
423 	return entry;
424 }
425 
426 /**
427  * Retrieves a given message resource, and returns the specified message number
428  */
getMessage(int resNum,int lineNum,Common::String & result,bool suppressErrors)429 bool TLib::getMessage(int resNum, int lineNum, Common::String &result, bool suppressErrors) {
430 	byte *msgData = getResource(RES_MESSAGE, resNum, 0, true);
431 	if (!msgData || (lineNum < 0)) {
432 		if (suppressErrors)
433 			return false;
434 
435 		error("Unknown message %d line %d", resNum, lineNum);
436 	}
437 
438 	int msgSize = _memoryManager.getSize(msgData);
439 	const char *srcP = (const char *)msgData;
440 	const char *endP = srcP + msgSize;
441 
442 	while (lineNum-- > 0) {
443 		srcP += strlen(srcP) + 1;
444 
445 		if (srcP >= endP) {
446 			if (suppressErrors)
447 				return false;
448 
449 			error("Unknown message %d line %d", resNum, lineNum);
450 		}
451 	}
452 
453 	result = Common::String(srcP);
454 	_memoryManager.deallocate(msgData);
455 	return true;
456 }
457 
458 /*--------------------------------------------------------------------------*/
459 
~ResourceManager()460 ResourceManager::~ResourceManager() {
461 	for (uint idx = 0; idx < _libList.size(); ++idx)
462 		delete _libList[idx];
463 }
464 
addLib(const Common::String & libName)465 void ResourceManager::addLib(const Common::String &libName) {
466 	assert(_libList.size() < 5);
467 
468 	_libList.push_back(new TLib(g_vm->_memoryManager, libName));
469 }
470 
getResource(uint16 id,bool suppressErrors)471 byte *ResourceManager::getResource(uint16 id, bool suppressErrors) {
472 	byte *result = NULL;
473 	for (uint idx = 0; idx < _libList.size(); ++idx) {
474 		result = _libList[idx]->getResource(id, true);
475 		if (result)
476 			return result;
477 	}
478 
479 	if (!result && !suppressErrors)
480 		error("Could not find resource Id #%d", id);
481 	return NULL;
482 }
483 
getResource(ResourceType resType,uint16 resNum,uint16 rlbNum,bool suppressErrors)484 byte *ResourceManager::getResource(ResourceType resType, uint16 resNum, uint16 rlbNum, bool suppressErrors) {
485 	byte *result = NULL;
486 	for (uint idx = 0; idx < _libList.size(); ++idx) {
487 		result = _libList[idx]->getResource(resType, resNum, rlbNum, true);
488 		if (result)
489 			return result;
490 	}
491 
492 	if (!result && !suppressErrors)
493 		error("Unknown resource type %d num %d", resType, resNum);
494 	return NULL;
495 }
496 
getPalette(int paletteNum,byte * palData,uint * startNum,uint * numEntries,bool suppressErrors)497 void ResourceManager::getPalette(int paletteNum, byte *palData, uint *startNum, uint *numEntries, bool suppressErrors) {
498 	for (uint idx = 0; idx < _libList.size(); ++idx) {
499 		if (_libList[idx]->getPalette(paletteNum, palData, startNum, numEntries))
500 			return;
501 	}
502 
503 	if (!suppressErrors)
504 		error("Unknown palette resource %d", paletteNum);
505 	*numEntries = 0;
506 }
507 
getSubResource(int resNum,int rlbNum,int index,uint * size,bool suppressErrors)508 byte *ResourceManager::getSubResource(int resNum, int rlbNum, int index, uint *size, bool suppressErrors) {
509 	byte *result = NULL;
510 	for (uint idx = 0; idx < _libList.size(); ++idx) {
511 		result = _libList[idx]->getSubResource(resNum, rlbNum, index, size, true);
512 		if (result)
513 			return result;
514 	}
515 
516 	if (!result && !suppressErrors)
517 		error("Unknown resource %d/%d index %d", resNum, rlbNum, index);
518 	return NULL;
519 }
520 
getMessage(int resNum,int lineNum,bool suppressErrors)521 Common::String ResourceManager::getMessage(int resNum, int lineNum, bool suppressErrors) {
522 	Common::String result;
523 
524 	for (uint idx = 0; idx < _libList.size(); ++idx) {
525 		if (_libList[idx]->getMessage(resNum, lineNum, result, true))
526 			return result;
527 	}
528 
529 	if (!suppressErrors)
530 		error("Unknown message %d line %d", resNum, lineNum);
531 	return Common::String();
532 }
533 
534 /*--------------------------------------------------------------------------*/
535 
536 /**
537  * Open up the given resource file using a passed file object. If the desired entry is found
538  * in the index, return the index entry for it, and move the file to the start of the resource
539  */
scanIndex(Common::File & f,ResourceType resType,int rlbNum,int resNum,ResourceEntry & resEntry)540 bool ResourceManager::scanIndex(Common::File &f, ResourceType resType, int rlbNum, int resNum,
541 									  ResourceEntry &resEntry) {
542 	// Load the root section index
543 	ResourceList resList;
544 	loadSection(f, resList);
545 
546 	// Loop through the index for the desired entry
547 	ResourceList::iterator iter;
548 	for (iter = resList.begin(); iter != resList.end(); ++iter) {
549 		ResourceEntry &re = *iter;
550 		if (re.id == resNum) {
551 			// Found it, so exit
552 			resEntry = re;
553 			f.seek(re.fileOffset);
554 			return true;
555 		}
556 	}
557 
558 	// No matching entry found
559 	return false;
560 }
561 
562 /**
563  * Inner logic for decoding a section index into a passed resource list object
564  */
loadSection(Common::File & f,ResourceList & resources)565 void ResourceManager::loadSection(Common::File &f, ResourceList &resources) {
566 	if (f.readUint32BE() != 0x544D492D)
567 		error("Data block is not valid Rlb data");
568 
569 	/*uint8 unknown1 = */f.readByte();
570 	uint16 numEntries = f.readByte();
571 
572 	for (uint i = 0; i < numEntries; ++i) {
573 		uint16 id = f.readUint16LE();
574 		uint16 size = f.readUint16LE();
575 		uint16 uncSize = f.readUint16LE();
576 		uint8 sizeHi = f.readByte();
577 		uint8 type = f.readByte() >> 5;
578 		assert(type <= 1);
579 		uint32 offset = f.readUint32LE();
580 
581 		ResourceEntry re;
582 		re.id = id;
583 		re.fileOffset = offset;
584 		re.isCompressed = type != 0;
585 		re.size = ((sizeHi & 0xF) << 16) | size;
586 		re.uncompressedSize = ((sizeHi & 0xF0) << 12) | uncSize;
587 
588 		resources.push_back(re);
589 	}
590 }
591 
592 } // end of namespace TsAGE
593