aboutsummaryrefslogtreecommitdiff
path: root/engines/lure/disk.cpp
blob: 282b231371e2ec1568392267d818c4731745bcc0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * $URL$
 * $Id$
 *
 */


#include "common/endian.h"
#include "common/file.h"
#include "common/util.h"
#include "common/scummsys.h"

#include "lure/disk.h"
#include "lure/lure.h"
#include "lure/luredefs.h"
#include "lure/res.h"

namespace Lure {

static Disk *int_disk = NULL;

Disk &Disk::getReference() {
	return *int_disk;
}

Disk::Disk() {
	_fileNum = 0xff;
	_fileHandle = NULL;
	int_disk = this;
}

Disk::~Disk() {
	if (_fileHandle) delete _fileHandle;
	int_disk = NULL;
}

uint8 Disk::indexOf(uint16 id, bool suppressError) {
	// Make sure the correct file is open - the upper two bits of the Id give the file number. Note
	// that an extra check is done for the upper byte of the Id being 0x3f, which is the Id range
	// I use for lure.dat resources, which are resources extracted from the lure.exe executable
	uint8 entryFileNum = ((id>>8) == 0x3f) ? 0 : ((id >> 14) & 3) + 1;
	openFile(entryFileNum);

	// Find the correct entry in the list based on the Id
	for (int entryIndex=0; entryIndex<NUM_ENTRIES_IN_HEADER; ++entryIndex) {
		if (_entries[entryIndex].id == HEADER_ENTRY_UNUSED_ID) break;
		else if (_entries[entryIndex].id == id) return entryIndex;
	}

	if (suppressError) return 0xff;
	if (_fileNum == 0)
		error("Could not find entry Id #%d in file %s", id, SUPPORT_FILENAME);
	else
		error("Could not find entry Id #%d in file disk%d.%s", id, _fileNum,
			LureEngine::getReference().isEGA() ? "ega" : "vga");
}

void Disk::openFile(uint8 fileNum) {
	// Validate that the file number is correct
	bool isEGA = LureEngine::getReference().isEGA();
	if (fileNum > 4)
		error("Invalid file number specified - %d", fileNum);

	// Only load up the new file if the current file number has changed
	if (fileNum == _fileNum) return;

	// Delete any existing open file handle
	if (_fileNum != 0xff) delete _fileHandle;
	_fileNum = fileNum;

	// Open up the the new file
	_fileHandle = new Common::File();

	char sFilename[10];
	if (_fileNum == 0)
		strcpy(sFilename, SUPPORT_FILENAME);
	else
		sprintf(sFilename, "disk%d.%s", _fileNum, isEGA ? "ega" : "vga");

	_fileHandle->open(sFilename);
	if (!_fileHandle->isOpen())
		error("Could not open %s", sFilename);

	char buffer[7];
	uint32 bytesRead;

	// If it's the support file, then move to the correct language area

	_dataOffset = 0;
	if (_fileNum == 0) {
		// Validate overall header
		_fileHandle->read(buffer, 6);
		buffer[4] = '\0';

		if (strcmp(buffer, SUPPORT_IDENT_STRING) != 0)
			error("The file %s is not a valid Lure support file", sFilename);

		// Scan for the correct language block
		Common::Language language = LureEngine::getReference().getLanguage();
		bool foundFlag = false;

		while (!foundFlag) {
			_fileHandle->read(buffer, 5);
			if ((byte)buffer[0] == 0xff)
				error("Could not find language data in support file");

			if ((language == (Common::Language)buffer[0]) || (language == UNK_LANG)) {
				foundFlag = true;
				_dataOffset = READ_LE_UINT32(&buffer[1]);
				_fileHandle->seek(_dataOffset);
			}
		}
	}

	// Validate the header

	bytesRead = _fileHandle->read(buffer, 6);
	buffer[6] = '\0';
	if (strcmp(buffer, HEADER_IDENT_STRING) != 0)
		error("The file %s was not a valid VGA file", sFilename);

	uint16 fileFileNum = _fileHandle->readUint16BE();
	if ((fileFileNum != 0) && (fileFileNum != (isEGA ? _fileNum + 4 : _fileNum)))
		error("The file %s was not the correct file number", sFilename);

	// Read in the header entries
	uint32 headerSize = sizeof(FileEntry) * NUM_ENTRIES_IN_HEADER;
	if (_fileHandle->read(_entries, headerSize) != headerSize)
		error("The file %s had a corrupted header", sFilename);

#ifdef SCUMM_BIG_ENDIAN
	// Process the read in header list to convert to big endian
	for (int i = 0; i < NUM_ENTRIES_IN_HEADER; ++i) {
		_entries[i].id = FROM_LE_16(_entries[i].id);
		_entries[i].size = FROM_LE_16(_entries[i].size);
		_entries[i].offset = FROM_LE_16(_entries[i].offset);
	}
#endif
}

uint32 Disk::getEntrySize(uint16 id) {
	// Special room area check
	uint16 tempId = id & 0x3fff;
	if ((tempId == 0x120) || (tempId == 0x311) || (tempId == 8) || (tempId == 0x410)) {
		ValueTableData &fieldList = Resources::getReference().fieldList();
		if (fieldList.getField(AREA_FLAG) != 0)
			id ^= 0x8000;
	}

	// Get the index of the resource, if necessary opening the correct file
	uint8 index = indexOf(id);

	// Calculate the offset and size of the entry
	uint32 size = (uint32) _entries[index].size;
	if (_entries[index].sizeExtension) size += 0x10000;

	return size;
}

MemoryBlock *Disk::getEntry(uint16 id) {
	// Special room area check
	uint16 tempId = id & 0x3fff;
	if ((tempId == 0x120) || (tempId == 0x311) || (tempId == 8) || (tempId == 0x410)) {
		ValueTableData &fieldList = Resources::getReference().fieldList();
		if (fieldList.getField(AREA_FLAG) != 0)
			id ^= 0x8000;
	}

	// Get the index of the resource, if necessary opening the correct file
	uint8 index = indexOf(id);

	// Calculate the offset and size of the entry
	uint32 size = (uint32) _entries[index].size;
	if (_entries[index].sizeExtension) size += 0x10000;
	uint32 offset = (uint32) _entries[index].offset * 0x20 + _dataOffset;

	MemoryBlock *result = Memory::allocate(size);
	_fileHandle->seek(offset, SEEK_SET);
	_fileHandle->read(result->data(), size);
	return result;
}

bool Disk::exists(uint16 id) {
	// Get the index of the resource, if necessary opening the correct file
	uint8 index = indexOf(id, true);
	return (index != 0xff);
}

uint8 Disk::numEntries() {
	if (_fileNum == 0)
		error("No file is currently open");

	// Figure out how many entries there are by count until an unused entry is found
	for (byte entryIndex = 0; entryIndex < NUM_ENTRIES_IN_HEADER; ++entryIndex)
		if (_entries[entryIndex].id == HEADER_ENTRY_UNUSED_ID) return entryIndex;

	return NUM_ENTRIES_IN_HEADER;
}

FileEntry *Disk::getIndex(uint8 entryIndex) {
	if (_fileNum == 0)
		error("No file is currently open");
	if ((entryIndex >= NUM_ENTRIES_IN_HEADER) || (_entries[entryIndex].id == HEADER_ENTRY_UNUSED_ID))
		error("There is no entry at the specified index");

	return &_entries[entryIndex];
}

} // end of namespace Lure