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/memstream.h"
24 #include "common/system.h"
25 #include "common/substream.h"
26 #include "common/tokenizer.h"
27 
28 #include "graphics/surface.h"
29 
30 #include "image/bmp.h"
31 
32 #include "petka/flc.h"
33 #include "petka/q_manager.h"
34 #include "petka/petka.h"
35 
36 namespace Petka {
37 
QManager(PetkaEngine & vm)38 QManager::QManager(PetkaEngine &vm)
39 	: _vm(vm) {}
40 
init()41 bool QManager::init() {
42 	clear();
43 
44 	Common::ScopedPtr<Common::SeekableReadStream> stream(_vm.openFile("resource.qrc", true));
45 	if (!stream) {
46 		return false;
47 	}
48 
49 	while (!stream->eos()) {
50 		Common::StringTokenizer tokenizer(stream->readLine());
51 		if (tokenizer.empty()) {
52 			continue;
53 		}
54 
55 		const uint32 id = (uint32)atoi(tokenizer.nextToken().c_str());
56 		_isAlwaysNeededMap.setVal(id, tokenizer.nextToken() == "==");
57 		_nameMap.setVal(id, tokenizer.nextToken());
58 
59 	}
60 	return true;
61 }
62 
findResourceName(uint32 id) const63 Common::String QManager::findResourceName(uint32 id) const {
64 	return _nameMap.contains(id) ? _nameMap.getVal(id) : "";
65 }
66 
findSoundName(uint32 id) const67 Common::String QManager::findSoundName(uint32 id) const {
68 	Common::String name = findResourceName(id);
69 	name.toUppercase();
70 	if (name.empty() || name.hasSuffix(".WAV")) {
71 		return name;
72 	}
73 	name.erase(name.size() - 3, 3);
74 	return name += "WAV";
75 }
76 
removeResource(uint32 id)77 void QManager::removeResource(uint32 id) {
78 	if (_resourceMap.contains(id)) {
79 		_resourceMap.erase(id);
80 	}
81 }
82 
clearUnneeded()83 void QManager::clearUnneeded() {
84 	for (auto it = _resourceMap.begin(); it != _resourceMap.end(); ++it) {
85 		if (!_isAlwaysNeededMap.getVal(it->_key)) {
86 			_resourceMap.erase(it);
87 		}
88 	}
89 }
90 
getSurface(uint32 id,uint16 w,uint16 h)91 Graphics::Surface *QManager::getSurface(uint32 id, uint16 w, uint16 h) {
92 	if (_resourceMap.contains(id)) {
93 		const QResource &res = _resourceMap.getVal(id);
94 		return res.type == QResource::kSurface ? res.surface : nullptr;
95 	}
96 
97 	QResource &res = _resourceMap.getOrCreateVal(id);
98 	res.type = QResource::kSurface;
99 	res.surface = new Graphics::Surface;
100 	res.surface->create(w, h, _vm._system->getScreenFormat());
101 
102 	return res.surface;
103 }
104 
loadFileStream(uint32 id) const105 Common::SeekableReadStream *QManager::loadFileStream(uint32 id) const {
106 	const Common::String &name = findResourceName(id);
107 	return name.empty() ? nullptr : _vm.openFile(name, false);
108 }
109 
getSurface(uint32 id)110 Graphics::Surface *QManager::getSurface(uint32 id) {
111 	if (_resourceMap.contains(id)) {
112 		const QResource &res = _resourceMap.getVal(id);
113 		return res.type == QResource::kSurface ? res.surface : nullptr;
114 	}
115 
116 	Common::ScopedPtr<Common::SeekableReadStream> stream(loadFileStream(id));
117 	if (!stream) {
118 		return nullptr;
119 	}
120 
121 	Graphics::Surface *s = loadBitmapSurface(*stream);
122 	if (s) {
123 		QResource &res = _resourceMap.getOrCreateVal(id);
124 		res.type = QResource::kSurface;
125 		res.surface = s;
126 		return res.surface;
127 	}
128 
129 	return nullptr;
130 }
131 
getFlic(uint32 id)132 FlicDecoder *QManager::getFlic(uint32 id) {
133 	if (_resourceMap.contains(id)) {
134 		const QResource &res = _resourceMap.getVal(id);
135 		return res.type == QResource::kFlic ? res.flcDecoder : nullptr;
136 	}
137 
138 	Common::String name = findResourceName(id);
139 	Common::SeekableReadStream *stream = _vm.openFile(name, false);
140 	if (!stream) {
141 		return nullptr;
142 	}
143 
144 	name.erase(name.size() - 3, 3);
145 	name.toUppercase();
146 	name += "MSK";
147 
148 	FlicDecoder *flc = new FlicDecoder;
149 	flc->load(stream, _vm.openFile(name, false));
150 
151 	QResource &res = _resourceMap.getOrCreateVal(id);
152 	res.type = QResource::kFlic;
153 	res.flcDecoder = flc;
154 
155 	return res.flcDecoder;
156 }
157 
clear()158 void QManager::clear() {
159 	_resourceMap.clear();
160 	_nameMap.clear();
161 	_isAlwaysNeededMap.clear();
162 }
163 
loadBitmapSurface(Common::SeekableReadStream & stream)164 Graphics::Surface *QManager::loadBitmapSurface(Common::SeekableReadStream &stream) {
165 	const uint32 kHeaderSize = 14 + 40;
166 	const uint32 kAdditionalDataSize = 8;
167 
168 	if (stream.readByte() != 'B')
169 		return nullptr;
170 
171 	if (stream.readByte() != 'M')
172 		return nullptr;
173 
174 	uint32 realFileSize = stream.readUint32LE();
175 
176 	stream.skip(12);
177 
178 	uint32 width = stream.readUint32LE();
179 	uint32 height = stream.readUint32LE();
180 
181 	stream.skip(2);
182 
183 	uint16 bitsPerPixel = stream.readUint16LE();
184 	if (bitsPerPixel != 16 && bitsPerPixel != 1) {
185 		stream.seek(0, SEEK_SET);
186 		Image::BitmapDecoder decoder;
187 		if (!decoder.loadStream(stream))
188 			return nullptr;
189 		return decoder.getSurface()->convertTo(g_system->getScreenFormat(), decoder.getPalette());
190 	}
191 	else if (bitsPerPixel == 1) {
192 		Graphics::Surface *s = new Graphics::Surface;
193 		s->create(width, height, Graphics::PixelFormat(2, 5, 6, 5, 0, 0, 5, 11, 0));
194 		return s;
195 	}
196 
197 	stream.seek(0, SEEK_SET);
198 	byte *convertedBmp = new byte[realFileSize];
199 
200 	stream.read(convertedBmp, kHeaderSize);
201 	WRITE_LE_INT16(convertedBmp + 28, 24); // bitsPerPixel
202 
203 	uint32 align = stream.readUint32LE();
204 	uint32 fileSize = stream.readUint32LE();
205 
206 	byte *pixels = convertedBmp + kHeaderSize;
207 	uint32 pixelsCount = (fileSize - (kHeaderSize + kAdditionalDataSize) - align + 1) / 2;
208 
209 	Graphics::PixelFormat fmt(2, 5, 6, 5, 0, 0, 5, 11, 0);
210 	while (pixelsCount) {
211 		fmt.colorToRGB(stream.readUint16BE(), *(pixels + 2), *(pixels + 1), *pixels);
212 		pixels += 3;
213 		pixelsCount--;
214 	}
215 
216 	Common::MemoryReadStream convBmpStream(convertedBmp, realFileSize, DisposeAfterUse::YES);
217 	Image::BitmapDecoder decoder;
218 	if (!decoder.loadStream(convBmpStream))
219 		return nullptr;
220 
221 	return decoder.getSurface()->convertTo(g_system->getScreenFormat(), decoder.getPalette());
222 }
223 
~QResource()224 QManager::QResource::~QResource() {
225 	if (type == QResource::kSurface && surface) {
226 		surface->free();
227 		delete surface;
228 	} else {
229 		delete flcDecoder;
230 	}
231 }
232 
233 } // End of namespace Petka
234