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/dcl.h"
24 #include "neverhood/blbarchive.h"
25 
26 namespace Neverhood {
27 
28 /**
29  * A special variant of SafeSeekableSubReadStream which locks a mutex during each read.
30  * This is neccessary because the music is streamed from disk and it could happen
31  * that a sound effect or another music track is played from the same read stream
32  * while the first music track is updated/read.
33  */
34 
35 class SafeMutexedSeekableSubReadStream : public Common::SafeSeekableSubReadStream {
36 public:
SafeMutexedSeekableSubReadStream(SeekableReadStream * parentStream,uint32 begin,uint32 end,DisposeAfterUse::Flag disposeParentStream,Common::Mutex & mutex)37 	SafeMutexedSeekableSubReadStream(SeekableReadStream *parentStream, uint32 begin, uint32 end, DisposeAfterUse::Flag disposeParentStream,
38 		Common::Mutex &mutex)
39 		: SafeSeekableSubReadStream(parentStream, begin, end, disposeParentStream), _mutex(mutex) {
40 	}
41 	uint32 read(void *dataPtr, uint32 dataSize) override;
42 protected:
43 	Common::Mutex &_mutex;
44 };
45 
read(void * dataPtr,uint32 dataSize)46 uint32 SafeMutexedSeekableSubReadStream::read(void *dataPtr, uint32 dataSize) {
47 	Common::StackLock lock(_mutex);
48 	return Common::SafeSeekableSubReadStream::read(dataPtr, dataSize);
49 }
50 
BlbArchive()51 BlbArchive::BlbArchive() : _extData(NULL) {
52 }
53 
~BlbArchive()54 BlbArchive::~BlbArchive() {
55 	delete[] _extData;
56 }
57 
open(const Common::String & filename)58 void BlbArchive::open(const Common::String &filename) {
59 	BlbHeader header;
60 	uint16 *extDataOffsets;
61 
62 	_entries.clear();
63 
64 	if (!_fd.open(filename))
65 		error("BlbArchive::open() Could not open %s", filename.c_str());
66 
67 	header.id1 = _fd.readUint32LE();
68 	header.id2 = _fd.readUint16LE();
69 	header.extDataSize = _fd.readUint16LE();
70 	header.fileSize = _fd.readUint32LE();
71 	header.fileCount = _fd.readUint32LE();
72 
73 	if (header.id1 != 0x2004940 || header.id2 != 7 || header.fileSize != _fd.size())
74 		error("BlbArchive::open() %s seems to be corrupt", filename.c_str());
75 
76 	debug(4, "%s: fileCount = %d", filename.c_str(), header.fileCount);
77 
78 	_entries.reserve(header.fileCount);
79 
80 	// Load file hashes
81 	for (uint i = 0; i < header.fileCount; i++) {
82 		BlbArchiveEntry entry;
83 		entry.fileHash = _fd.readUint32LE();
84 		_entries.push_back(entry);
85 	}
86 
87 	extDataOffsets = new uint16[header.fileCount];
88 
89 	// Load file records
90 	for (uint i = 0; i < header.fileCount; i++) {
91 		BlbArchiveEntry &entry = _entries[i];
92 		entry.type = _fd.readByte();
93 		entry.comprType = _fd.readByte();
94 		entry.extData = NULL;
95 		extDataOffsets[i] = _fd.readUint16LE();
96 		entry.timeStamp = _fd.readUint32LE();
97 		entry.offset = _fd.readUint32LE();
98 		entry.diskSize = _fd.readUint32LE();
99 		entry.size = _fd.readUint32LE();
100 		debug(4, "%08X: %03d, %02X, %04X, %08X, %08X, %08X, %08X",
101 			entry.fileHash, entry.type, entry.comprType, extDataOffsets[i], entry.timeStamp,
102 			entry.offset, entry.diskSize, entry.size);
103 	}
104 
105 	// Load ext data
106 	if (header.extDataSize > 0) {
107 		_extData = new byte[header.extDataSize];
108 		_fd.read(_extData, header.extDataSize);
109 		for (uint i = 0; i < header.fileCount; i++)
110 			_entries[i].extData = extDataOffsets[i] > 0 ? _extData + extDataOffsets[i] - 1 : NULL;
111 	}
112 
113 	delete[] extDataOffsets;
114 
115 }
116 
load(uint index,byte * buffer,uint32 size)117 void BlbArchive::load(uint index, byte *buffer, uint32 size) {
118 	load(&_entries[index], buffer, size);
119 }
120 
load(BlbArchiveEntry * entry,byte * buffer,uint32 size)121 void BlbArchive::load(BlbArchiveEntry *entry, byte *buffer, uint32 size) {
122 	Common::StackLock lock(_mutex);
123 
124 	_fd.seek(entry->offset);
125 
126 	switch (entry->comprType) {
127 	case 1: // Uncompressed
128 		if (size == 0)
129 			size = entry->diskSize;
130 		_fd.read(buffer, size);
131 		break;
132 	case 3: // DCL-compressed
133 		if (!Common::decompressDCL(&_fd, buffer, entry->diskSize, entry->size))
134 			error("BlbArchive::load() Error during decompression of %08X (offset: %d, disk size: %d, size: %d)",
135 					entry->fileHash, entry->offset, entry->diskSize, entry->size);
136 		break;
137 	default:
138 		error("BlbArchive::load() Unknown compression type %d", entry->comprType);
139 	}
140 
141 }
142 
getEntryExtData(uint index)143 byte *BlbArchive::getEntryExtData(uint index) {
144 	return getEntryExtData(&_entries[index]);
145 }
146 
getEntryExtData(BlbArchiveEntry * entry)147 byte *BlbArchive::getEntryExtData(BlbArchiveEntry *entry) {
148 	return entry->extData;
149 }
150 
createStream(uint index)151 Common::SeekableReadStream *BlbArchive::createStream(uint index) {
152 	return createStream(&_entries[index]);
153 }
154 
createStream(BlbArchiveEntry * entry)155 Common::SeekableReadStream *BlbArchive::createStream(BlbArchiveEntry *entry) {
156 	return new SafeMutexedSeekableSubReadStream(&_fd, entry->offset, entry->offset + entry->diskSize,
157 		DisposeAfterUse::NO, _mutex);
158 }
159 
160 } // End of namespace Neverhood
161