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