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 
24 #include "queen/bankman.h"
25 #include "queen/resource.h"
26 
27 #include "common/debug.h"
28 
29 namespace Queen {
30 
BankManager(Resource * res)31 BankManager::BankManager(Resource *res)
32 	: _res(res) {
33 	memset(_frames, 0, sizeof(_frames));
34 	memset(_banks, 0, sizeof(_banks));
35 }
36 
~BankManager()37 BankManager::~BankManager() {
38 	for (uint32 i = 0; i < MAX_BANKS_NUMBER; ++i) {
39 		close(i);
40 	}
41 	eraseFrames(true);
42 }
43 
load(const char * bankname,uint32 bankslot)44 void BankManager::load(const char *bankname, uint32 bankslot) {
45 	debug(9, "BankManager::load(%s, %d)", bankname, bankslot);
46 
47 	assert(bankslot < MAX_BANKS_NUMBER);
48 	PackedBank *bank = &_banks[bankslot];
49 
50 	if (!scumm_stricmp(bankname, bank->name)) {
51 		debug(9, "BankManager::load() bank '%s' already loaded", bankname);
52 		return;
53 	}
54 
55 	close(bankslot);
56 
57 	if (_res->getPlatform() == Common::kPlatformAmiga && !_res->fileExists(bankname)) {
58 		debug(9, "BankManager::load() bank '%s' doesn't exist", bankname);
59 		return;
60 	}
61 
62 	bank->data = _res->loadFile(bankname);
63 
64 	if (_res->getPlatform() == Common::kPlatformAmiga) {
65 		uint16 entries = READ_BE_UINT16(bank->data + 4);
66 		debug(9, "BankManager::load() entries = %d", entries);
67 		assert(entries < MAX_BANK_SIZE);
68 		uint32 offset = 6;
69 		_banks[bankslot].indexes[0] = offset;
70 		for (uint16 i = 1; i <= entries; ++i) {
71 			_banks[bankslot].indexes[i] = offset;
72 			uint16 dataSize = READ_BE_UINT16(bank->data + offset + 10);
73 			offset += dataSize + 12;
74 		}
75 	} else {
76 		uint16 entries = READ_LE_UINT16(bank->data);
77 		debug(9, "BankManager::load() entries = %d", entries);
78 		assert(entries < MAX_BANK_SIZE);
79 		uint32 offset = 2;
80 		_banks[bankslot].indexes[0] = offset;
81 		for (uint16 i = 1; i <= entries; ++i) {
82 			_banks[bankslot].indexes[i] = offset;
83 			uint16 w = READ_LE_UINT16(bank->data + offset + 0);
84 			uint16 h = READ_LE_UINT16(bank->data + offset + 2);
85 			offset += w * h + 8;
86 		}
87 	}
88 
89 	// mark this bank as loaded
90 	strcpy(bank->name, bankname);
91 }
92 
convertPlanarBitmap(uint8 * dst,int dstPitch,const uint8 * src,int w,int h,int plane)93 static void convertPlanarBitmap(uint8 *dst, int dstPitch, const uint8 *src, int w, int h, int plane) {
94 	assert(w != 0 && h != 0);
95 	int planarSize = plane * h * w * 2;
96 	uint8 *planarBuf = new uint8[ planarSize ];
97 	uint8 *dstPlanar = planarBuf;
98 	while (planarSize > 0) {
99 		if (src[0] == 0) {
100 			int count = src[1];
101 			memset(dstPlanar, 0, count);
102 			dstPlanar += count;
103 			src += 2;
104 			planarSize -= count;
105 		} else {
106 			*dstPlanar++ = *src++;
107 			--planarSize;
108 		}
109 	}
110 
111 	src = planarBuf;
112 	int i = 0;
113 	int planeSize = h * w * 2;
114 	while (h--) {
115 		for (int x = 0; x < w * 2; ++x) {
116 			for (int b = 0; b < 8; ++b) {
117 				const uint8 mask = (1 << (7 - b));
118 				uint8 color = 0;
119 				for (int p = 0; p < plane; ++p) {
120 					if (src[planeSize * p + i] & mask) {
121 						color |= (1 << p);
122 					}
123 				}
124 				dst[8 * x + b] = color;
125 			}
126 			++i;
127 		}
128 		dst += dstPitch;
129 	}
130 
131 	delete[] planarBuf;
132 }
133 
unpack(uint32 srcframe,uint32 dstframe,uint32 bankslot)134 void BankManager::unpack(uint32 srcframe, uint32 dstframe, uint32 bankslot) {
135 	debug(9, "BankManager::unpack(%d, %d, %d)", srcframe, dstframe, bankslot);
136 
137 	assert(bankslot < MAX_BANKS_NUMBER);
138 	PackedBank *bank = &_banks[bankslot];
139 	assert(bank->data != NULL);
140 
141 	assert(dstframe < MAX_FRAMES_NUMBER);
142 	BobFrame *bf = &_frames[dstframe];
143 	delete[] bf->data;
144 	bf->data = NULL;
145 
146 	const uint8 *p = bank->data + bank->indexes[srcframe];
147 
148 	if (_res->getPlatform() == Common::kPlatformAmiga) {
149 		uint16 w     = READ_BE_UINT16(p + 0);
150 		uint16 h     = READ_BE_UINT16(p + 2);
151 		uint16 plane = READ_BE_UINT16(p + 4);
152 		bf->xhotspot = READ_BE_UINT16(p + 6);
153 		bf->yhotspot = READ_BE_UINT16(p + 8);
154 		bf->width    = w * 16;
155 		bf->height   = h;
156 
157 		uint32 size = bf->width * bf->height;
158 		if (size != 0) {
159 			bf->data = new uint8[ size ];
160 			convertPlanarBitmap(bf->data, bf->width, p + 12, w, h, plane);
161 		}
162 	} else {
163 		bf->width    = READ_LE_UINT16(p + 0);
164 		bf->height   = READ_LE_UINT16(p + 2);
165 		bf->xhotspot = READ_LE_UINT16(p + 4);
166 		bf->yhotspot = READ_LE_UINT16(p + 6);
167 
168 		uint32 size = bf->width * bf->height;
169 		if (size != 0) {
170 			bf->data = new uint8[ size ];
171 			memcpy(bf->data, p + 8, size);
172 		}
173 	}
174 }
175 
overpack(uint32 srcframe,uint32 dstframe,uint32 bankslot)176 void BankManager::overpack(uint32 srcframe, uint32 dstframe, uint32 bankslot) {
177 	debug(9, "BankManager::overpack(%d, %d, %d)", srcframe, dstframe, bankslot);
178 
179 	assert(bankslot < MAX_BANKS_NUMBER);
180 	PackedBank *bank = &_banks[bankslot];
181 	assert(bank->data != NULL);
182 
183 	assert(dstframe < MAX_FRAMES_NUMBER);
184 	BobFrame *bf = &_frames[dstframe];
185 
186 	const uint8 *p = bank->data + bank->indexes[srcframe];
187 
188 	if (_res->getPlatform() == Common::kPlatformAmiga) {
189 		uint16 w     = READ_BE_UINT16(p + 0);
190 		uint16 h     = READ_BE_UINT16(p + 2);
191 		uint16 plane = READ_BE_UINT16(p + 4);
192 		uint16 src_w = w * 16;
193 		uint16 src_h = h;
194 
195 		if (bf->width < src_w || bf->height < src_h) {
196 			unpack(srcframe, dstframe, bankslot);
197 		} else {
198 			convertPlanarBitmap(bf->data, bf->width, p + 12, w, h, plane);
199 		}
200 	} else {
201 		uint16 src_w = READ_LE_UINT16(p + 0);
202 		uint16 src_h = READ_LE_UINT16(p + 2);
203 
204 		// unpack if destination frame is smaller than source
205 		if (bf->width < src_w || bf->height < src_h) {
206 			unpack(srcframe, dstframe, bankslot);
207 		} else {
208 			// copy data 'over' destination frame (without updating frame header)
209 			memcpy(bf->data, p + 8, src_w * src_h);
210 		}
211 	}
212 }
213 
close(uint32 bankslot)214 void BankManager::close(uint32 bankslot) {
215 	debug(9, "BankManager::close(%d)", bankslot);
216 	assert(bankslot < MAX_BANKS_NUMBER);
217 	PackedBank *bank = &_banks[bankslot];
218 	delete[] bank->data;
219 	memset(bank, 0, sizeof(PackedBank));
220 }
221 
fetchFrame(uint32 index)222 BobFrame *BankManager::fetchFrame(uint32 index) {
223 	debug(9, "BankManager::fetchFrame(%d)", index);
224 	assert(index < MAX_FRAMES_NUMBER);
225 	BobFrame *bf = &_frames[index];
226 	assert((bf->width == 0 && bf->height == 0) || bf->data != 0);
227 	return bf;
228 }
229 
eraseFrame(uint32 index)230 void BankManager::eraseFrame(uint32 index) {
231 	debug(9, "BankManager::eraseFrame(%d)", index);
232 	assert(index < MAX_FRAMES_NUMBER);
233 	BobFrame *bf = &_frames[index];
234 	delete[] bf->data;
235 	memset(bf, 0, sizeof(BobFrame));
236 }
237 
eraseFrames(bool joe)238 void BankManager::eraseFrames(bool joe) {
239 	for (uint32 i = joe ? 0 : FRAMES_JOE; i < MAX_FRAMES_NUMBER; ++i) {
240 		eraseFrame(i);
241 	}
242 }
243 
244 } // End of namespace Queen
245