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 "common/debug.h"
25 #include "common/textconsole.h"
26 #include "common/endian.h"
27 #include "common/file.h"
28 
29 #include "sky/disk.h"
30 #include "sky/sky.h"
31 #include "sky/struc.h"
32 
33 namespace Sky {
34 
35 static const char *const dataFilename = "sky.dsk";
36 static const char *const dinnerFilename = "sky.dnr";
37 
Disk()38 Disk::Disk() {
39 	_dataDiskHandle = new Common::File();
40 	Common::File *dnrHandle = new Common::File();
41 
42 	dnrHandle->open(dinnerFilename);
43 	if (!dnrHandle->isOpen())
44 		error("Could not open %s", dinnerFilename);
45 
46 	if (!(_dinnerTableEntries = dnrHandle->readUint32LE()))
47 		error("Error reading from sky.dnr"); //even though it was opened correctly?!
48 
49 	_dinnerTableArea = (uint8 *)malloc(_dinnerTableEntries * 8);
50 	uint32 entriesRead = dnrHandle->read(_dinnerTableArea, 8 * _dinnerTableEntries) / 8;
51 
52 	if (entriesRead != _dinnerTableEntries)
53 		error("entriesRead != dinnerTableEntries. [%d/%d]", entriesRead, _dinnerTableEntries);
54 
55 	_dataDiskHandle->open(dataFilename);
56 	if (!_dataDiskHandle->isOpen())
57 		error("Error opening %s", dataFilename);
58 
59 	debug("Found BASS version v0.0%d (%d dnr entries)", determineGameVersion(), _dinnerTableEntries);
60 
61 	memset(_buildList, 0, 60 * 2);
62 	memset(_loadedFilesList, 0, 60 * 4);
63 
64 	dnrHandle->close();
65 	delete dnrHandle;
66 }
67 
~Disk()68 Disk::~Disk() {
69 	if (_dataDiskHandle->isOpen())
70 		_dataDiskHandle->close();
71 	fnFlushBuffers();
72 	free(_dinnerTableArea);
73 	delete _dataDiskHandle;
74 }
75 
fileExists(uint16 fileNr)76 bool Disk::fileExists(uint16 fileNr) {
77 	return (getFileInfo(fileNr) != NULL);
78 }
79 
80 // allocate memory, load the file and return a pointer
loadFile(uint16 fileNr)81 uint8 *Disk::loadFile(uint16 fileNr) {
82 	uint8 cflag;
83 
84 	debug(3, "load file %d,%d (%d)", (fileNr >> 11), (fileNr & 2047), fileNr);
85 
86 	uint8 *fileInfoPtr = getFileInfo(fileNr);
87 	if (fileInfoPtr == NULL) {
88 		debug(1, "File %d not found", fileNr);
89 		return NULL;
90 	}
91 
92 	uint32 fileFlags = READ_LE_UINT24(fileInfoPtr + 5);
93 	uint32 fileSize = fileFlags & 0x03fffff;
94 	uint32 fileOffset = READ_LE_UINT32(fileInfoPtr + 2) & 0x0ffffff;
95 
96 	_lastLoadedFileSize = fileSize;
97 	cflag = (uint8)((fileOffset >> 23) & 0x1);
98 	fileOffset &= 0x7FFFFF;
99 
100 	if (cflag) {
101 		if (SkyEngine::_systemVars.gameVersion == 331)
102 			fileOffset <<= 3;
103 		else
104 			fileOffset <<= 4;
105 	}
106 
107 	uint8 *fileDest = (uint8 *)malloc(fileSize + 4); // allocate memory for file
108 
109 	_dataDiskHandle->seek(fileOffset, SEEK_SET);
110 
111 	//now read in the data
112 	int32 bytesRead = _dataDiskHandle->read(fileDest, fileSize);
113 
114 	if (bytesRead != (int32)fileSize)
115 		warning("Unable to read %d bytes from datadisk (%d bytes read)", fileSize, bytesRead);
116 
117 	cflag = (uint8)((fileFlags >> 23) & 0x1);
118 	//if cflag == 0 then file is compressed, 1 == uncompressed
119 
120 	DataFileHeader *fileHeader = (DataFileHeader *)fileDest;
121 
122 	if ((!cflag) && ((FROM_LE_16(fileHeader->flag) >> 7) & 1)) {
123 		debug(4, "File is RNC compressed.");
124 
125 		uint32 decompSize = (FROM_LE_16(fileHeader->flag) & ~0xFF) << 8;
126 		decompSize |= FROM_LE_16(fileHeader->s_tot_size);
127 
128 		uint8 *uncompDest = (uint8 *)malloc(decompSize);
129 
130 		int32 unpackLen;
131 		void *output, *input = fileDest + sizeof(DataFileHeader);
132 
133 		if ((fileFlags >> 22) & 0x1) { //do we include the header?
134 			// don't return the file's header
135 			output = uncompDest;
136 			unpackLen = _rncDecoder.unpackM1(input, output, 0);
137 		} else {
138 #ifdef SCUMM_BIG_ENDIAN
139 			// Convert DataFileHeader to BE (it only consists of 16 bit words)
140 			uint16 *headPtr = (uint16 *)fileDest;
141 			for (uint i = 0; i < sizeof(DataFileHeader) / 2; i++)
142 				*(headPtr + i) = READ_LE_UINT16(headPtr + i);
143 #endif
144 
145 			memcpy(uncompDest, fileDest, sizeof(DataFileHeader));
146 			output = uncompDest + sizeof(DataFileHeader);
147 			unpackLen = _rncDecoder.unpackM1(input, output, 0);
148 			if (unpackLen)
149 				unpackLen += sizeof(DataFileHeader);
150 		}
151 
152 		debug(5, "UnpackM1 returned: %d", unpackLen);
153 
154 		if (unpackLen == 0) { //Unpack returned 0: file was probably not packed.
155 			free(uncompDest);
156 			return fileDest;
157 		} else {
158 			if (unpackLen != (int32)decompSize)
159 				debug(1, "ERROR: File %d: invalid decomp size! (was: %d, should be: %d)", fileNr, unpackLen, decompSize);
160 			_lastLoadedFileSize = decompSize;
161 
162 			free(fileDest);
163 			return uncompDest;
164 		}
165 	} else {
166 #ifdef SCUMM_BIG_ENDIAN
167 		if (!cflag) {
168 			uint16 *headPtr = (uint16 *)fileDest;
169 			for (uint i = 0; i < sizeof(DataFileHeader) / 2; i++)
170 				*(headPtr + i) = READ_LE_UINT16(headPtr + i);
171 		}
172 #endif
173 		return fileDest;
174 	}
175 }
176 
loadScriptFile(uint16 fileNr)177 uint16 *Disk::loadScriptFile(uint16 fileNr) {
178 	uint16 *buf = (uint16 *)loadFile(fileNr);
179 #ifdef SCUMM_BIG_ENDIAN
180 	for (uint i = 0; i < _lastLoadedFileSize / 2; i++)
181 		buf[i] = FROM_LE_16(buf[i]);
182 #endif
183 	return buf;
184 }
185 
getFileInfo(uint16 fileNr)186 uint8 *Disk::getFileInfo(uint16 fileNr) {
187 	uint16 i;
188 	uint16 *dnrTbl16Ptr = (uint16 *)_dinnerTableArea;
189 
190 	for (i = 0; i < _dinnerTableEntries; i++) {
191 		if (READ_LE_UINT16(dnrTbl16Ptr) == fileNr) {
192 			debug(4, "file %d found", fileNr);
193 			return (uint8 *)dnrTbl16Ptr;
194 		}
195 		dnrTbl16Ptr += 4;
196 	}
197 
198 	return 0; //not found
199 }
200 
fnCacheChip(uint16 * fList)201 void Disk::fnCacheChip(uint16 *fList) {
202 	// fnCacheChip is called after fnCacheFast
203 	uint16 cnt = 0;
204 	while (_buildList[cnt])
205 		cnt++;
206 	uint16 fCnt = 0;
207 	do {
208 		_buildList[cnt + fCnt] = fList[fCnt] & 0x7FFFU;
209 		fCnt++;
210 	} while (fList[fCnt-1]);
211 	fnCacheFiles();
212 }
213 
fnCacheFast(uint16 * fList)214 void Disk::fnCacheFast(uint16 *fList) {
215 	if (fList != NULL) {
216 		uint8 cnt = 0;
217 		do {
218 			_buildList[cnt] = fList[cnt] & 0x7FFFU;
219 			cnt++;
220 		} while (fList[cnt-1]);
221 	}
222 }
223 
fnCacheFiles()224 void Disk::fnCacheFiles() {
225 	uint16 lCnt, bCnt, targCnt;
226 	targCnt = lCnt = 0;
227 	bool found;
228 	while (_loadedFilesList[lCnt]) {
229 		bCnt = 0;
230 		found = false;
231 		while (_buildList[bCnt] && (!found)) {
232 			if ((_buildList[bCnt] & 0x7FFFU) == _loadedFilesList[lCnt])
233 				found = true;
234 			else
235 				bCnt++;
236 		}
237 		if (found) {
238 			_loadedFilesList[targCnt] = _loadedFilesList[lCnt];
239 			targCnt++;
240 		} else {
241 			free(SkyEngine::_itemList[_loadedFilesList[lCnt] & 2047]);
242 			SkyEngine::_itemList[_loadedFilesList[lCnt] & 2047] = NULL;
243 		}
244 		lCnt++;
245 	}
246 	_loadedFilesList[targCnt] = 0; // mark end of list
247 	bCnt = 0;
248 	while (_buildList[bCnt]) {
249 		if ((_buildList[bCnt] & 0x7FF) == 0x7FF) {
250 			// amiga dummy files
251 			bCnt++;
252 			continue;
253 		}
254 		lCnt = 0;
255 		found = false;
256 		while (_loadedFilesList[lCnt] && (!found)) {
257 			if (_loadedFilesList[lCnt] == (_buildList[bCnt] & 0x7FFFU))
258 				found = true;
259 			lCnt++;
260 		}
261 		if (found) {
262 			bCnt++;
263 			continue;
264 		}
265 		// ok, we really have to load the file.
266 		_loadedFilesList[targCnt] = _buildList[bCnt] & 0x7FFFU;
267 		targCnt++;
268 		_loadedFilesList[targCnt] = 0;
269 		SkyEngine::_itemList[_buildList[bCnt] & 2047] = (void**)loadFile(_buildList[bCnt] & 0x7FFF);
270 		if (!SkyEngine::_itemList[_buildList[bCnt] & 2047])
271 			warning("fnCacheFiles: Disk::loadFile() returned NULL for file %d",_buildList[bCnt] & 0x7FFF);
272 		bCnt++;
273 	}
274 	_buildList[0] = 0;
275 }
276 
refreshFilesList(uint32 * list)277 void Disk::refreshFilesList(uint32 *list) {
278 	uint8 cnt = 0;
279 	while (_loadedFilesList[cnt]) {
280 		if (SkyEngine::_itemList[_loadedFilesList[cnt] & 2047])
281 			free(SkyEngine::_itemList[_loadedFilesList[cnt] & 2047]);
282 		SkyEngine::_itemList[_loadedFilesList[cnt] & 2047] = NULL;
283 		cnt++;
284 	}
285 	cnt = 0;
286 	while (list[cnt]) {
287 		_loadedFilesList[cnt] = list[cnt];
288 		SkyEngine::_itemList[_loadedFilesList[cnt] & 2047] = (void**)loadFile((uint16)(_loadedFilesList[cnt] & 0x7FFF));
289 		cnt++;
290 	}
291 	_loadedFilesList[cnt] = 0;
292 }
293 
fnMiniLoad(uint16 fileNum)294 void Disk::fnMiniLoad(uint16 fileNum) {
295 	uint16 cnt = 0;
296 	while (_loadedFilesList[cnt]) {
297 		if (_loadedFilesList[cnt] == fileNum)
298 			return;
299 		cnt++;
300 	}
301 	_loadedFilesList[cnt] = fileNum & 0x7FFFU;
302 	_loadedFilesList[cnt + 1] = 0;
303 	SkyEngine::_itemList[fileNum & 2047] = (void**)loadFile(fileNum);
304 }
305 
fnFlushBuffers()306 void Disk::fnFlushBuffers() {
307 	// dump all loaded sprites
308 	uint8 lCnt = 0;
309 	while (_loadedFilesList[lCnt]) {
310 		free(SkyEngine::_itemList[_loadedFilesList[lCnt] & 2047]);
311 		SkyEngine::_itemList[_loadedFilesList[lCnt] & 2047] = NULL;
312 		lCnt++;
313 	}
314 	_loadedFilesList[0] = 0;
315 }
316 
dumpFile(uint16 fileNr)317 void Disk::dumpFile(uint16 fileNr) {
318 	char buf[128];
319 	Common::DumpFile out;
320 	byte* filePtr;
321 
322 	filePtr = loadFile(fileNr);
323 	sprintf(buf, "dumps/file-%d.dmp", fileNr);
324 
325 	if (!Common::File::exists(buf)) {
326 		if (out.open(buf))
327 			out.write(filePtr, _lastLoadedFileSize);
328 	}
329 	free(filePtr);
330 }
331 
determineGameVersion()332 uint32 Disk::determineGameVersion() {
333 	//determine game version based on number of entries in dinner table
334 	switch (_dinnerTableEntries) {
335 	case 232:
336 		// German floppy demo (v0.0272)
337 		return 272;
338 	case 243:
339 		// pc gamer demo (v0.0109)
340 		return 109;
341 	case 247:
342 		// English floppy demo (v0.0267)
343 		return 267;
344 	case 1404:
345 		//floppy (v0.0288)
346 		return 288;
347 	case 1413:
348 		//floppy (v0.0303)
349 		return 303;
350 	case 1445:
351 		//floppy (v0.0331 or v0.0348)
352 		if (_dataDiskHandle->size() == 8830435)
353 			return 348;
354 		else
355 			return 331;
356 	case 1711:
357 		//cd demo (v0.0365)
358 		return 365;
359 	case 5099:
360 		//cd (v0.0368)
361 		return 368;
362 	case 5097:
363 		//cd (v0.0372)
364 		return 372;
365 	default:
366 		//unknown version
367 		error("Unknown game version! %d dinner table entries", _dinnerTableEntries);
368 		break;
369 	}
370 }
371 
372 } // End of namespace Sky
373