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/algorithm.h"
24 #include "common/memstream.h"
25 #include "neverhood/resource.h"
26 #include "neverhood/resourceman.h"
27 
28 namespace Neverhood {
29 
30 // SpriteResource
31 
SpriteResource(NeverhoodEngine * vm)32 SpriteResource::SpriteResource(NeverhoodEngine *vm)
33 	 : _vm(vm), _pixels(NULL) {
34 }
35 
~SpriteResource()36 SpriteResource::~SpriteResource() {
37 	unload();
38 }
39 
draw(Graphics::Surface * destSurface,bool flipX,bool flipY)40 void SpriteResource::draw(Graphics::Surface *destSurface, bool flipX, bool flipY) {
41 	if (_pixels) {
42 		byte *dest = (byte*)destSurface->getPixels();
43 		const int destPitch = destSurface->pitch;
44 		if (_rle)
45 			unpackSpriteRle(_pixels, _dimensions.width, _dimensions.height, dest, destPitch, flipX, flipY);
46 		else
47 			unpackSpriteNormal(_pixels, _dimensions.width, _dimensions.height, dest, destPitch, flipX, flipY);
48 	}
49 }
50 
load(uint32 fileHash,bool doLoadPosition)51 bool SpriteResource::load(uint32 fileHash, bool doLoadPosition) {
52 	debug(2, "SpriteResource::load(%08X)", fileHash);
53 	unload();
54 	_vm->_res->queryResource(fileHash, _resourceHandle);
55 	if (_resourceHandle.isValid() && _resourceHandle.type() == kResTypeBitmap) {
56 		_vm->_res->loadResource(_resourceHandle, _vm->applyResourceFixes());
57 		const byte *spriteData = _resourceHandle.data();
58 		NPoint *position = doLoadPosition ? &_position : NULL;
59 		parseBitmapResource(spriteData, &_rle, &_dimensions, position, NULL, &_pixels);
60 	}
61 	return _pixels != NULL;
62 }
63 
unload()64 void SpriteResource::unload() {
65 	_vm->_res->unloadResource(_resourceHandle);
66 	_pixels = NULL;
67 	_rle = false;
68 }
69 
70 // PaletteResource
71 
PaletteResource(NeverhoodEngine * vm)72 PaletteResource::PaletteResource(NeverhoodEngine *vm)
73 	: _vm(vm), _palette(NULL) {
74 }
75 
~PaletteResource()76 PaletteResource::~PaletteResource() {
77 	unload();
78 }
79 
load(uint32 fileHash)80 bool PaletteResource::load(uint32 fileHash) {
81 	debug(2, "PaletteResource::load(%08X)", fileHash);
82 	unload();
83 	_vm->_res->queryResource(fileHash, _resourceHandle);
84 	if (_resourceHandle.isValid() &&
85 		(_resourceHandle.type() == kResTypeBitmap || _resourceHandle.type() == kResTypePalette)) {
86 		_vm->_res->loadResource(_resourceHandle, _vm->applyResourceFixes());
87 		_palette = _resourceHandle.data();
88 		// Check if the palette is stored in a bitmap
89 		if (_resourceHandle.type() == kResTypeBitmap)
90 			parseBitmapResource(_palette, NULL, NULL, NULL, &_palette, NULL);
91 
92 	}
93 	return _palette != NULL;
94 }
95 
unload()96 void PaletteResource::unload() {
97 	_vm->_res->unloadResource(_resourceHandle);
98 	_palette = NULL;
99 }
100 
copyPalette(byte * destPalette)101 void PaletteResource::copyPalette(byte *destPalette) {
102 	if (_palette)
103 		memcpy(destPalette, _palette, 1024);
104 }
105 
106 // AnimResource
107 
AnimResource(NeverhoodEngine * vm)108 AnimResource::AnimResource(NeverhoodEngine *vm)
109 	: _vm(vm), _width(0), _height(0), _currSpriteData(NULL), _fileHash(0), _paletteData(NULL),
110 	_spriteData(NULL), _replEnabled(false), _replOldColor(0), _replNewColor(0) {
111 }
112 
~AnimResource()113 AnimResource::~AnimResource() {
114 	unload();
115 }
116 
draw(uint frameIndex,Graphics::Surface * destSurface,bool flipX,bool flipY)117 void AnimResource::draw(uint frameIndex, Graphics::Surface *destSurface, bool flipX, bool flipY) {
118 	const AnimFrameInfo frameInfo = _frames[frameIndex];
119 	byte *dest = (byte*)destSurface->getPixels();
120 	const int destPitch = destSurface->pitch;
121 	_currSpriteData = _spriteData + frameInfo.spriteDataOffs;
122 	_width = frameInfo.drawOffset.width;
123 	_height = frameInfo.drawOffset.height;
124 	if (_replEnabled && _replOldColor != _replNewColor)
125 		unpackSpriteRle(_currSpriteData, _width, _height, dest, destPitch, flipX, flipY, _replOldColor, _replNewColor);
126 	else
127 		unpackSpriteRle(_currSpriteData, _width, _height, dest, destPitch, flipX, flipY);
128 }
129 
load(uint32 fileHash)130 bool AnimResource::load(uint32 fileHash) {
131 	debug(2, "AnimResource::load(%08X)", fileHash);
132 
133 	if (fileHash == _fileHash)
134 		return true;
135 
136 	unload();
137 
138 	_vm->_res->queryResource(fileHash, _resourceHandle);
139 	if (!_resourceHandle.isValid() || _resourceHandle.type() != kResTypeAnimation)
140 		return false;
141 
142 	const byte *resourceData, *animList, *frameList;
143 	uint16 animInfoStartOfs, animListIndex, animListCount;
144 	uint16 frameListStartOfs, frameCount;
145 	uint32 spriteDataOfs, paletteDataOfs;
146 
147 	_vm->_res->loadResource(_resourceHandle, _vm->applyResourceFixes());
148 	resourceData = _resourceHandle.data();
149 
150 	animListCount = READ_LE_UINT16(resourceData);
151 	animInfoStartOfs = READ_LE_UINT16(resourceData + 2);
152 	spriteDataOfs = READ_LE_UINT32(resourceData + 4);
153 	paletteDataOfs = READ_LE_UINT32(resourceData + 8);
154 
155 	animList = resourceData + 12;
156 	for (animListIndex = 0; animListIndex < animListCount; animListIndex++) {
157 		debug(8, "hash: %08X", READ_LE_UINT32(animList));
158 		if (READ_LE_UINT32(animList) == fileHash)
159 			break;
160 		animList += 8;
161 	}
162 
163 	if (animListIndex >= animListCount) {
164 		_vm->_res->unloadResource(_resourceHandle);
165 		return false;
166 	}
167 
168 	_spriteData = resourceData + spriteDataOfs;
169 	if (paletteDataOfs > 0)
170 		_paletteData = resourceData + paletteDataOfs;
171 
172 	frameCount = READ_LE_UINT16(animList + 4);
173 	frameListStartOfs = READ_LE_UINT16(animList + 6);
174 
175 	debug(8, "frameCount = %d; frameListStartOfs = %04X; animInfoStartOfs = %04X", frameCount, frameListStartOfs, animInfoStartOfs);
176 
177 	frameList = resourceData + animInfoStartOfs + frameListStartOfs;
178 
179 	_frames.clear();
180 	_frames.reserve(frameCount);
181 
182 	for (uint16 frameIndex = 0; frameIndex < frameCount; frameIndex++) {
183 		AnimFrameInfo frameInfo;
184 		frameInfo.frameHash = READ_LE_UINT32(frameList);
185 		frameInfo.counter = READ_LE_UINT16(frameList + 4);
186 		frameInfo.drawOffset.x = READ_LE_UINT16(frameList + 6);
187 		frameInfo.drawOffset.y = READ_LE_UINT16(frameList + 8);
188 		frameInfo.drawOffset.width = READ_LE_UINT16(frameList + 10);
189 		frameInfo.drawOffset.height = READ_LE_UINT16(frameList + 12);
190 		frameInfo.deltaX = READ_LE_UINT16(frameList + 14);
191 		frameInfo.deltaY = READ_LE_UINT16(frameList + 16);
192 		frameInfo.collisionBoundsOffset.x = READ_LE_UINT16(frameList + 18);
193 		frameInfo.collisionBoundsOffset.y = READ_LE_UINT16(frameList + 20);
194 		frameInfo.collisionBoundsOffset.width = READ_LE_UINT16(frameList + 22);
195 		frameInfo.collisionBoundsOffset.height = READ_LE_UINT16(frameList + 24);
196 		frameInfo.spriteDataOffs = READ_LE_UINT32(frameList + 28);
197 		debug(8, "frameHash = %08X; counter = %d; rect = (%d,%d,%d,%d); deltaX = %d; deltaY = %d; collisionBoundsOffset = (%d,%d,%d,%d); spriteDataOffs = %08X",
198 			frameInfo.frameHash, frameInfo.counter,
199 			frameInfo.drawOffset.x, frameInfo.drawOffset.y, frameInfo.drawOffset.width, frameInfo.drawOffset.height,
200 			frameInfo.deltaX, frameInfo.deltaY,
201 			frameInfo.collisionBoundsOffset.x, frameInfo.collisionBoundsOffset.y, frameInfo.collisionBoundsOffset.width, frameInfo.collisionBoundsOffset.height,
202 			frameInfo.spriteDataOffs);
203 		frameList += 32;
204 		_frames.push_back(frameInfo);
205 	}
206 
207 	_fileHash = fileHash;
208 
209 	return true;
210 
211 }
212 
unload()213 void AnimResource::unload() {
214 	_vm->_res->unloadResource(_resourceHandle);
215 	_currSpriteData = NULL;
216 	_fileHash = 0;
217 	_paletteData = NULL;
218 	_spriteData = NULL;
219 	_replEnabled = true;
220 	_replOldColor = 0;
221 	_replNewColor = 0;
222 }
223 
getFrameIndex(uint32 frameHash)224 int16 AnimResource::getFrameIndex(uint32 frameHash) {
225 	int16 frameIndex = -1;
226 	for (uint i = 0; i < _frames.size(); i++)
227 		if (_frames[i].frameHash == frameHash) {
228 			frameIndex = (int16)i;
229 			break;
230 		}
231 	debug(2, "AnimResource::getFrameIndex(%08X) -> %d", frameHash, frameIndex);
232 	return frameIndex;
233 }
234 
setRepl(byte oldColor,byte newColor)235 void AnimResource::setRepl(byte oldColor, byte newColor) {
236 	_replOldColor = oldColor;
237 	_replNewColor = newColor;
238 }
239 
loadSpriteDimensions(uint32 fileHash)240 NDimensions AnimResource::loadSpriteDimensions(uint32 fileHash) {
241 	ResourceHandle resourceHandle;
242 	NDimensions dimensions;
243 	_vm->_res->queryResource(fileHash, resourceHandle);
244 	const byte *resDimensions = resourceHandle.extData();
245 	if (resDimensions) {
246 		dimensions.width = READ_LE_UINT16(resDimensions + 0);
247 		dimensions.height = READ_LE_UINT16(resDimensions + 2);
248 	} else {
249 		dimensions.width = 0;
250 		dimensions.height = 0;
251 	}
252 	return dimensions;
253 }
254 
255 // MouseCursorResource
256 
MouseCursorResource(NeverhoodEngine * vm)257 MouseCursorResource::MouseCursorResource(NeverhoodEngine *vm)
258 	: _cursorSprite(vm), _cursorNum(4), _currFileHash(0) {
259 
260 	_rect.width = 32;
261 	_rect.height = 32;
262 }
263 
load(uint32 fileHash)264 void MouseCursorResource::load(uint32 fileHash) {
265 	if (_currFileHash != fileHash) {
266 		if (_cursorSprite.load(fileHash) && !_cursorSprite.isRle() &&
267 			_cursorSprite.getDimensions().width == 96 && _cursorSprite.getDimensions().height == 224) {
268 			_currFileHash = fileHash;
269 		} else {
270 			unload();
271 		}
272 	}
273 }
274 
unload()275 void MouseCursorResource::unload() {
276 	_cursorSprite.unload();
277 	_currFileHash = 0;
278 	_cursorNum = 4;
279 }
280 
getRect()281 NDrawRect& MouseCursorResource::getRect() {
282 	static const NPoint kCursorHotSpots[] = {
283 		{-15, -5},
284 		{-17, -25},
285 		{-17, -30},
286 		{-14, -1},
287 		{-3, -7},
288 		{-30, -18},
289 		{-1, -18}
290 	};
291 	_rect.x = kCursorHotSpots[_cursorNum].x;
292 	_rect.y = kCursorHotSpots[_cursorNum].y;
293 	return _rect;
294 }
295 
draw(int frameNum,Graphics::Surface * destSurface)296 void MouseCursorResource::draw(int frameNum, Graphics::Surface *destSurface) {
297 	if (_cursorSprite.getPixels()) {
298 		const int sourcePitch = (_cursorSprite.getDimensions().width + 3) & 0xFFFC; // 4 byte alignment
299 		const int destPitch = destSurface->pitch;
300 		const byte *source = _cursorSprite.getPixels() + _cursorNum * (sourcePitch * 32) + frameNum * 32;
301 		byte *dest = (byte*)destSurface->getPixels();
302 		for (int16 yc = 0; yc < 32; yc++) {
303 			memcpy(dest, source, 32);
304 			source += sourcePitch;
305 			dest += destPitch;
306 		}
307 	}
308 }
309 
310 // TextResource
311 
TextResource(NeverhoodEngine * vm)312 TextResource::TextResource(NeverhoodEngine *vm)
313 	: _vm(vm), _textData(NULL), _count(0) {
314 
315 }
316 
~TextResource()317 TextResource::~TextResource() {
318 	unload();
319 }
320 
load(uint32 fileHash)321 void TextResource::load(uint32 fileHash) {
322 	debug(2, "TextResource::load(%08X)", fileHash);
323 	unload();
324 	_vm->_res->queryResource(fileHash, _resourceHandle);
325 	if (_resourceHandle.isValid() && _resourceHandle.type() == kResTypeText) {
326 		_vm->_res->loadResource(_resourceHandle, _vm->applyResourceFixes());
327 		_textData = _resourceHandle.data();
328 		_count = READ_LE_UINT32(_textData);
329 	}
330 }
331 
unload()332 void TextResource::unload() {
333 	_vm->_res->unloadResource(_resourceHandle);
334 	_textData = NULL;
335 	_count = 0;
336 }
337 
getString(uint index,const char * & textEnd)338 const char *TextResource::getString(uint index, const char *&textEnd) {
339 	const char *textStart = (const char*)(_textData + 4 + _count * 4 + READ_LE_UINT32(_textData + (index + 1) * 4));
340 	textEnd = (const char*)(_textData + 4 + _count * 4 + READ_LE_UINT32(_textData + (index + 2) * 4));
341 	return textStart;
342 }
343 
344 // DataResource
345 
DataResource(NeverhoodEngine * vm)346 DataResource::DataResource(NeverhoodEngine *vm)
347 	: _vm(vm) {
348 }
349 
~DataResource()350 DataResource::~DataResource() {
351 	unload();
352 }
353 
load(uint32 fileHash)354 void DataResource::load(uint32 fileHash) {
355 	if (_resourceHandle.fileHash() == fileHash)
356 		return;
357 	const byte *data = NULL;
358 	uint32 dataSize = 0;
359 	unload();
360 	_vm->_res->queryResource(fileHash, _resourceHandle);
361 	if (_resourceHandle.isValid() && _resourceHandle.type() == kResTypeData) {
362 		_vm->_res->loadResource(_resourceHandle, _vm->applyResourceFixes());
363 		data = _resourceHandle.data();
364 		dataSize = _resourceHandle.size();
365 	}
366 	if (data && dataSize) {
367 		Common::MemoryReadStream dataS(data, dataSize);
368 		uint itemCount = dataS.readUint16LE();
369 		uint32 itemStartOffs = 2 + itemCount * 8;
370 		debug(2, "itemCount = %d", itemCount);
371 		for (uint i = 0; i < itemCount; i++) {
372 			dataS.seek(2 + i * 8);
373 			DRDirectoryItem drDirectoryItem;
374 			drDirectoryItem.nameHash = dataS.readUint32LE();
375 			drDirectoryItem.offset = dataS.readUint16LE();
376 			drDirectoryItem.type = dataS.readUint16LE();
377 			debug(2, "%03d nameHash = %08X; offset = %04X; type = %d", i, drDirectoryItem.nameHash, drDirectoryItem.offset, drDirectoryItem.type);
378 			dataS.seek(itemStartOffs + drDirectoryItem.offset);
379 			switch (drDirectoryItem.type) {
380 			case 1:
381 				{
382 					debug(3, "NPoint");
383 					NPoint point;
384 					point.x = dataS.readUint16LE();
385 					point.y = dataS.readUint16LE();
386 					debug(3, "(%d, %d)", point.x, point.y);
387 					drDirectoryItem.offset = _points.size();
388 					_points.push_back(point);
389 					break;
390 				}
391 			case 2:
392 				{
393 					uint count = dataS.readUint16LE();
394 					NPointArray *pointArray = new NPointArray();
395 					debug(3, "NPointArray; count = %d", count);
396 					for (uint j = 0; j < count; j++) {
397 						NPoint point;
398 						point.x = dataS.readUint16LE();
399 						point.y = dataS.readUint16LE();
400 						debug(3, "(%d, %d)", point.x, point.y);
401 						pointArray->push_back(point);
402 					}
403 					drDirectoryItem.offset = _pointArrays.size();
404 					_pointArrays.push_back(pointArray);
405 					break;
406 				}
407 			case 3:
408 				{
409 					uint count = dataS.readUint16LE();
410 					HitRectList *hitRectList = new HitRectList();
411 					debug(3, "HitRectList; count = %d", count);
412 					for (uint j = 0; j < count; j++) {
413 						HitRect hitRect;
414 						hitRect.rect.x1 = dataS.readUint16LE();
415 						hitRect.rect.y1 = dataS.readUint16LE();
416 						hitRect.rect.x2 = dataS.readUint16LE();
417 						hitRect.rect.y2 = dataS.readUint16LE();
418 						hitRect.type = dataS.readUint16LE() + 0x5001;
419 						debug(3, "(%d, %d, %d, %d) -> %04d", hitRect.rect.x1, hitRect.rect.y1, hitRect.rect.x2, hitRect.rect.y2, hitRect.type);
420 						hitRectList->push_back(hitRect);
421 					}
422 					drDirectoryItem.offset = _hitRectLists.size();
423 					_hitRectLists.push_back(hitRectList);
424 					break;
425 				}
426 			case 4:
427 				{
428 					uint count = dataS.readUint16LE();
429 					MessageList *messageList = new MessageList();
430 					debug(3, "MessageList; count = %d", count);
431 					for (uint j = 0; j < count; j++) {
432 						MessageItem messageItem;
433 						messageItem.messageNum = dataS.readUint32LE();
434 						messageItem.messageValue = dataS.readUint32LE();
435 						debug(3, "(%08X, %08X)", messageItem.messageNum, messageItem.messageValue);
436 						messageList->push_back(messageItem);
437 					}
438 					drDirectoryItem.offset = _messageLists.size();
439 					_messageLists.push_back(messageList);
440 					break;
441 				}
442 			case 5:
443 				{
444 					uint count = dataS.readUint16LE();
445 					DRSubRectList *drSubRectList = new DRSubRectList();
446 					debug(3, "SubRectList; count = %d", count);
447 					for (uint j = 0; j < count; j++) {
448 						DRSubRect drSubRect;
449 						drSubRect.rect.x1 = dataS.readUint16LE();
450 						drSubRect.rect.y1 = dataS.readUint16LE();
451 						drSubRect.rect.x2 = dataS.readUint16LE();
452 						drSubRect.rect.y2 = dataS.readUint16LE();
453 						drSubRect.messageListHash = dataS.readUint32LE();
454 						drSubRect.messageListItemIndex = dataS.readUint16LE();
455 						debug(3, "(%d, %d, %d, %d) -> %08X (%d)", drSubRect.rect.x1, drSubRect.rect.y1, drSubRect.rect.x2, drSubRect.rect.y2, drSubRect.messageListHash, drSubRect.messageListItemIndex);
456 						drSubRectList->push_back(drSubRect);
457 					}
458 					drDirectoryItem.offset = _drSubRectLists.size();
459 					_drSubRectLists.push_back(drSubRectList);
460 					break;
461 				}
462 			case 6:
463 				{
464 					DRRect drRect;
465 					drRect.rect.x1 = dataS.readUint16LE();
466 					drRect.rect.y1 = dataS.readUint16LE();
467 					drRect.rect.x2 = dataS.readUint16LE();
468 					drRect.rect.y2 = dataS.readUint16LE();
469 					drRect.subRectIndex = dataS.readUint16LE();
470 					debug(3, "(%d, %d, %d, %d) -> %d", drRect.rect.x1, drRect.rect.y1, drRect.rect.x2, drRect.rect.y2, drRect.subRectIndex);
471 					drDirectoryItem.offset = _drRects.size();
472 					_drRects.push_back(drRect);
473 					break;
474 				}
475 			case 7:
476 				{
477 					uint count = dataS.readUint16LE();
478 					NRectArray *rectArray = new NRectArray();
479 					debug(3, "NRectArray; count = %d", count);
480 					for (uint j = 0; j < count; j++) {
481 						NRect rect;
482 						rect.x1 = dataS.readUint16LE();
483 						rect.y1 = dataS.readUint16LE();
484 						rect.x2 = dataS.readUint16LE();
485 						rect.y2 = dataS.readUint16LE();
486 						debug(3, "(%d, %d, %d, %d)", rect.x1, rect.y1, rect.x2, rect.y2);
487 						rectArray->push_back(rect);
488 					}
489 					drDirectoryItem.offset = _rectArrays.size();
490 					_rectArrays.push_back(rectArray);
491 					break;
492 				}
493 			default:
494 				break;
495 			}
496 			_directory.push_back(drDirectoryItem);
497 		}
498 	}
499 }
500 
unload()501 void DataResource::unload() {
502 	_directory.clear();
503 	_points.clear();
504 	for (Common::Array<NPointArray*>::iterator it = _pointArrays.begin(); it != _pointArrays.end(); ++it)
505 		delete (*it);
506 	_pointArrays.clear();
507 	for (Common::Array<NRectArray*>::iterator it = _rectArrays.begin(); it != _rectArrays.end(); ++it)
508 		delete (*it);
509 	_rectArrays.clear();
510 	for (Common::Array<HitRectList*>::iterator it = _hitRectLists.begin(); it != _hitRectLists.end(); ++it)
511 		delete (*it);
512 	_hitRectLists.clear();
513 	for (Common::Array<MessageList*>::iterator it = _messageLists.begin(); it != _messageLists.end(); ++it)
514 		delete (*it);
515 	_messageLists.clear();
516 	_drRects.clear();
517 	for (Common::Array<DRSubRectList*>::iterator it = _drSubRectLists.begin(); it != _drSubRectLists.end(); ++it)
518 		delete (*it);
519 	_drSubRectLists.clear();
520 	_vm->_res->unloadResource(_resourceHandle);
521 }
522 
getPoint(uint32 nameHash)523 NPoint DataResource::getPoint(uint32 nameHash) {
524 	DataResource::DRDirectoryItem *drDirectoryItem = findDRDirectoryItem(nameHash, 1);
525 	return drDirectoryItem ? _points[drDirectoryItem->offset] : NPoint();
526 }
527 
getPointArray(uint32 nameHash)528 NPointArray *DataResource::getPointArray(uint32 nameHash) {
529 	DataResource::DRDirectoryItem *drDirectoryItem = findDRDirectoryItem(nameHash, 2);
530 	return drDirectoryItem ? _pointArrays[drDirectoryItem->offset] : NULL;
531 }
532 
getRectArray(uint32 nameHash)533 NRectArray *DataResource::getRectArray(uint32 nameHash) {
534 	DataResource::DRDirectoryItem *drDirectoryItem = findDRDirectoryItem(nameHash, 3);
535 	return drDirectoryItem ? _rectArrays[drDirectoryItem->offset] : NULL;
536 }
537 
getHitRectList()538 HitRectList *DataResource::getHitRectList() {
539 	DataResource::DRDirectoryItem *drDirectoryItem = findDRDirectoryItem(calcHash("HitArray"), 3);
540 	return drDirectoryItem ? _hitRectLists[drDirectoryItem->offset] : NULL;
541 }
542 
getMessageListAtPos(int16 klaymenX,int16 klaymenY,int16 mouseX,int16 mouseY)543 MessageList *DataResource::getMessageListAtPos(int16 klaymenX, int16 klaymenY, int16 mouseX, int16 mouseY) {
544 	for (uint i = 0; i < _drRects.size(); i++) {
545 		if (klaymenX >= _drRects[i].rect.x1 && klaymenX <= _drRects[i].rect.x2 &&
546 			klaymenY >= _drRects[i].rect.y1 && klaymenY <= _drRects[i].rect.y2) {
547 			DRSubRectList *drSubRectList = _drSubRectLists[_drRects[i].subRectIndex];
548 			for (uint j = 0; j < drSubRectList->size(); j++) {
549 				DRSubRect &subRect = (*drSubRectList)[j];
550 				if (mouseX >= subRect.rect.x1 && mouseX <= subRect.rect.x2 &&
551 					mouseY >= subRect.rect.y1 && mouseY <= subRect.rect.y2) {
552 					return _messageLists[subRect.messageListItemIndex];
553 				}
554 			}
555 		}
556 	}
557 	return NULL;
558 }
559 
findDRDirectoryItem(uint32 nameHash,uint16 type)560 DataResource::DRDirectoryItem *DataResource::findDRDirectoryItem(uint32 nameHash, uint16 type) {
561 	for (Common::Array<DRDirectoryItem>::iterator it = _directory.begin(); it != _directory.end(); it++)
562 		if ((*it).nameHash == nameHash && (*it).type == type)
563 			return &(*it);
564 	return NULL;
565 }
566 
calcHash(const char * value)567 uint32 calcHash(const char *value) {
568 	uint32 hash = 0, shiftValue = 0;
569 	while (*value != 0) {
570 		char ch = *value++;
571 		if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')) {
572 			if (ch >= 'a' && ch <= 'z')
573 				ch -= 32;
574 			else if (ch >= '0' && ch <= '9')
575 				ch += 22;
576 			shiftValue += ch - 64;
577 			if (shiftValue >= 32)
578 				shiftValue -= 32;
579 			hash ^= 1 << shiftValue;
580 		}
581 	}
582 	return hash;
583 }
584 
585 } // End of namespace Neverhood
586