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/endian.h"
25 #include "common/file.h"
26 #include "common/util.h"
27 #include "common/scummsys.h"
28 
29 #include "lure/disk.h"
30 #include "lure/lure.h"
31 #include "lure/luredefs.h"
32 #include "lure/res.h"
33 
34 namespace Lure {
35 
36 static Disk *int_disk = NULL;
37 
getReference()38 Disk &Disk::getReference() {
39 	return *int_disk;
40 }
41 
Disk()42 Disk::Disk() {
43 	_fileNum = 0xff;
44 	_fileHandle = NULL;
45 	int_disk = this;
46 }
47 
~Disk()48 Disk::~Disk() {
49 	delete _fileHandle;
50 	int_disk = NULL;
51 }
52 
indexOf(uint16 id,bool suppressError)53 uint8 Disk::indexOf(uint16 id, bool suppressError) {
54 	// Make sure the correct file is open - the upper two bits of the Id give the file number. Note
55 	// that an extra check is done for the upper byte of the Id being 0x3f, which is the Id range
56 	// I use for lure.dat resources, which are resources extracted from the lure.exe executable
57 	uint8 entryFileNum = ((id>>8) == 0x3f) ? 0 : ((id >> 14) & 3) + 1;
58 	openFile(entryFileNum);
59 
60 	// Find the correct entry in the list based on the Id
61 	for (int entryIndex=0; entryIndex<NUM_ENTRIES_IN_HEADER; ++entryIndex) {
62 		if (_entries[entryIndex].id == HEADER_ENTRY_UNUSED_ID) break;
63 		else if (_entries[entryIndex].id == id) return entryIndex;
64 	}
65 
66 	if (suppressError) return 0xff;
67 	if (_fileNum == 0)
68 		error("Could not find entry Id #%d in file %s", id, SUPPORT_FILENAME);
69 	else
70 		error("Could not find entry Id #%d in file disk%d.%s", id, _fileNum,
71 			LureEngine::getReference().isEGA() ? "ega" : "vga");
72 }
73 
openFile(uint8 fileNum)74 void Disk::openFile(uint8 fileNum) {
75 	// Validate that the file number is correct
76 	bool isEGA = LureEngine::getReference().isEGA();
77 	if (fileNum > 4)
78 		error("Invalid file number specified - %d", fileNum);
79 
80 	// Only load up the new file if the current file number has changed
81 	if (fileNum == _fileNum) return;
82 
83 	// Delete any existing open file handle
84 	if (_fileNum != 0xff) delete _fileHandle;
85 	_fileNum = fileNum;
86 
87 	// Open up the the new file
88 	_fileHandle = new Common::File();
89 
90 	char sFilename[10];
91 	if (_fileNum == 0)
92 		strcpy(sFilename, SUPPORT_FILENAME);
93 	else
94 		sprintf(sFilename, "disk%d.%s", _fileNum, isEGA ? "ega" : "vga");
95 
96 	_fileHandle->open(sFilename);
97 	if (!_fileHandle->isOpen())
98 		error("Could not open %s", sFilename);
99 
100 	char buffer[7];
101 
102 	// If it's the support file, then move to the correct language area
103 
104 	_dataOffset = 0;
105 	if (_fileNum == 0) {
106 		// Validate overall header
107 		_fileHandle->read(buffer, 6);
108 		buffer[4] = '\0';
109 
110 		if (strcmp(buffer, SUPPORT_IDENT_STRING) != 0)
111 			error("The file %s is not a valid Lure support file", sFilename);
112 
113 		// Scan for the correct language block
114 		LureLanguage language = LureEngine::getReference().getLureLanguage();
115 		bool foundFlag = false;
116 
117 		while (!foundFlag) {
118 			_fileHandle->read(buffer, 5);
119 			if ((byte)buffer[0] == 0xff)
120 				error("Could not find language data in support file");
121 
122 			if ((language == (LureLanguage)buffer[0]) || (language == LANG_UNKNOWN)) {
123 				foundFlag = true;
124 				_dataOffset = READ_LE_UINT32(&buffer[1]);
125 				_fileHandle->seek(_dataOffset);
126 			}
127 		}
128 	}
129 
130 	// Validate the header
131 
132 	_fileHandle->read(buffer, 6);
133 	buffer[6] = '\0';
134 	if (strcmp(buffer, HEADER_IDENT_STRING) != 0)
135 		error("The file %s was not a valid VGA file", sFilename);
136 
137 	uint16 fileFileNum = _fileHandle->readUint16BE();
138 	if ((fileFileNum != 0) && (fileFileNum != (isEGA ? _fileNum + 4 : _fileNum)))
139 		error("The file %s was not the correct file number", sFilename);
140 
141 	// Read in the header entries
142 	uint32 headerSize = sizeof(FileEntry) * NUM_ENTRIES_IN_HEADER;
143 	if (_fileHandle->read(_entries, headerSize) != headerSize)
144 		error("The file %s had a corrupted header", sFilename);
145 
146 #ifdef SCUMM_BIG_ENDIAN
147 	// Process the read in header list to convert to big endian
148 	for (int i = 0; i < NUM_ENTRIES_IN_HEADER; ++i) {
149 		_entries[i].id = FROM_LE_16(_entries[i].id);
150 		_entries[i].size = FROM_LE_16(_entries[i].size);
151 		_entries[i].offset = FROM_LE_16(_entries[i].offset);
152 	}
153 #endif
154 }
155 
getEntrySize(uint16 id)156 uint32 Disk::getEntrySize(uint16 id) {
157 	// Special room area check
158 	uint16 tempId = id & 0x3fff;
159 	if ((tempId == 0x120) || (tempId == 0x311) || (tempId == 8) || (tempId == 0x410)) {
160 		ValueTableData &fieldList = Resources::getReference().fieldList();
161 		if (fieldList.getField(AREA_FLAG) != 0)
162 			id ^= 0x8000;
163 	}
164 
165 	// Get the index of the resource, if necessary opening the correct file
166 	uint8 index = indexOf(id);
167 
168 	// Calculate the offset and size of the entry
169 	uint32 size = (uint32) _entries[index].size;
170 	if (_entries[index].sizeExtension) size += 0x10000;
171 
172 	return size;
173 }
174 
getEntry(uint16 id)175 MemoryBlock *Disk::getEntry(uint16 id) {
176 	// Special room area check
177 	uint16 tempId = id & 0x3fff;
178 	if ((tempId == 0x120) || (tempId == 0x311) || (tempId == 8) || (tempId == 0x410)) {
179 		ValueTableData &fieldList = Resources::getReference().fieldList();
180 		if (fieldList.getField(AREA_FLAG) != 0)
181 			id ^= 0x8000;
182 	}
183 
184 	// Get the index of the resource, if necessary opening the correct file
185 	uint8 index = indexOf(id);
186 
187 	// Calculate the offset and size of the entry
188 	uint32 size = (uint32) _entries[index].size;
189 	if (_entries[index].sizeExtension) size += 0x10000;
190 	uint32 offset = (uint32) _entries[index].offset * 0x20 + _dataOffset;
191 
192 	MemoryBlock *result = Memory::allocate(size);
193 	_fileHandle->seek(offset, SEEK_SET);
194 	_fileHandle->read(result->data(), size);
195 	return result;
196 }
197 
exists(uint16 id)198 bool Disk::exists(uint16 id) {
199 	// Get the index of the resource, if necessary opening the correct file
200 	uint8 index = indexOf(id, true);
201 	return (index != 0xff);
202 }
203 
numEntries()204 uint8 Disk::numEntries() {
205 	if (_fileNum == 0)
206 		error("No file is currently open");
207 
208 	// Figure out how many entries there are by count until an unused entry is found
209 	for (byte entryIndex = 0; entryIndex < NUM_ENTRIES_IN_HEADER; ++entryIndex)
210 		if (_entries[entryIndex].id == HEADER_ENTRY_UNUSED_ID) return entryIndex;
211 
212 	return NUM_ENTRIES_IN_HEADER;
213 }
214 
getIndex(uint8 entryIndex)215 FileEntry *Disk::getIndex(uint8 entryIndex) {
216 	if (_fileNum == 0)
217 		error("No file is currently open");
218 	if ((entryIndex >= NUM_ENTRIES_IN_HEADER) || (_entries[entryIndex].id == HEADER_ENTRY_UNUSED_ID))
219 		error("There is no entry at the specified index");
220 
221 	return &_entries[entryIndex];
222 }
223 
224 } // End of namespace Lure
225