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 "glk/zcode/pics.h"
24 #include "glk/zcode/pics_decoder.h"
25 #include "glk/glk.h"
26 #include "common/algorithm.h"
27 #include "common/memstream.h"
28 
29 namespace Glk {
30 namespace ZCode {
31 
32 enum {
33 	PIC_FILE_HEADER_FLAGS      = 1,
34 	PIC_FILE_HEADER_NUM_IMAGES = 4,
35 	PIC_FILE_HEADER_ENTRY_SIZE = 8,
36 	PIC_FILE_HEADER_VERSION    = 14
37 };
38 
Pics()39 Pics::Pics() : Common::Archive(), _filename(getFilename()) {
40 	Common::File f;
41 	if (!f.open(_filename))
42 		error("Error reading Pics file");
43 
44 	_palette = new Common::Array<byte>();
45 
46 	Common::Array<uint> offsets;
47 	byte buffer[16];
48 	f.read(buffer, 16);
49 	_index.resize(READ_LE_UINT16(&buffer[PIC_FILE_HEADER_NUM_IMAGES]));
50 	_entrySize = buffer[PIC_FILE_HEADER_ENTRY_SIZE];
51 	_version = buffer[PIC_FILE_HEADER_FLAGS];
52 	assert(_entrySize >= 8 && _entrySize <= 14);
53 
54 	// Iterate through loading the index
55 	for (uint idx = 0; idx < _index.size(); ++idx) {
56 		Entry &e = _index[idx];
57 		f.read(buffer, _entrySize);
58 
59 		e._number = READ_LE_UINT16(buffer);
60 		e._width = READ_LE_UINT16(buffer + 2);
61 		e._height = READ_LE_UINT16(buffer + 4);
62 		e._flags = READ_LE_UINT16(buffer + 6);
63 
64 		if (_entrySize >= 11) {
65 			e._dataOffset = READ_BE_UINT32(buffer + 7) & 0xffffff;
66 			if (e._dataOffset)
67 				offsets.push_back(e._dataOffset);
68 
69 			if (_entrySize == 14) {
70 				e._paletteOffset = READ_BE_UINT32(buffer + 10) & 0xffffff;
71 			}
72 		}
73 
74 		if (e._dataOffset)
75 			e._filename = Common::String::format("pic%u.raw", e._number);
76 		else
77 			e._filename = Common::String::format("pic%u.rect", e._number);
78 	}
79 
80 	// Further processing of index to calculate data sizes
81 	Common::sort(offsets.begin(), offsets.end());
82 
83 	for (uint idx = 0; idx < _index.size(); ++idx) {
84 		Entry &e = _index[idx];
85 		if (!e._dataOffset)
86 			continue;
87 
88 		// Find the entry in the offsets array
89 		uint oidx = 0;
90 		while (oidx < offsets.size() && offsets[oidx] != e._dataOffset)
91 			++oidx;
92 
93 		// Set the size
94 		e._dataSize = (oidx == (offsets.size() - 1) ? f.size() : offsets[oidx + 1]) - e._dataOffset;
95 	}
96 
97 	f.close();
98 }
99 
~Pics()100 Pics::~Pics() {
101 	delete _palette;
102 }
103 
getFilename()104 Common::String Pics::getFilename() {
105 	Common::String filename = g_vm->getFilename();
106 	while (filename.contains('.'))
107 		filename.deleteLastChar();
108 
109 	return filename + ".mg1";
110 }
111 
exists()112 bool Pics::exists() {
113 	return Common::File::exists(getFilename());
114 }
115 
hasFile(const Common::Path & path) const116 bool Pics::hasFile(const Common::Path &path) const {
117 	Common::String name = path.toString();
118 	for (uint idx = 0; idx < _index.size(); ++idx) {
119 		if (_index[idx]._filename.equalsIgnoreCase(name))
120 			return true;
121 	}
122 
123 	return false;
124 }
125 
listMembers(Common::ArchiveMemberList & list) const126 int Pics::listMembers(Common::ArchiveMemberList &list) const {
127 	for (uint idx = 0; idx < _index.size(); ++idx) {
128 		list.push_back(Common::ArchiveMemberList::value_type(new Common::GenericArchiveMember(_index[idx]._filename, this)));
129 	}
130 
131 	return (int)_index.size();
132 }
133 
getMember(const Common::Path & path) const134 const Common::ArchiveMemberPtr Pics::getMember(const Common::Path &path) const {
135 	Common::String name = path.toString();
136 	if (!hasFile(name))
137 		return Common::ArchiveMemberPtr();
138 
139 	return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this));
140 }
141 
createReadStreamForMember(const Common::Path & path) const142 Common::SeekableReadStream *Pics::createReadStreamForMember(const Common::Path &path) const {
143 	Common::String name = path.toString();
144 	PictureDecoder decoder;
145 
146 	for (uint idx = 0; idx < _index.size(); ++idx) {
147 		const Entry &e = _index[idx];
148 		if (e._filename.equalsIgnoreCase(name)) {
149 			Common::File f;
150 			Common::SeekableReadStream *dest;
151 			if (!f.open(_filename))
152 				error("Reading failed");
153 
154 			if (e._dataSize) {
155 				loadPalette(f, e, *_palette);
156 
157 				f.seek(e._dataOffset);
158 				Common::SeekableReadStream *src = f.readStream(e._dataSize);
159 				dest = decoder.decode(*src, e._flags, *_palette, kMCGA, e._width, e._height);
160 				delete src;
161 			} else {
162 				byte *rect = (byte *)malloc(2 * sizeof(uint32));
163 				WRITE_BE_UINT32(rect, e._width);
164 				WRITE_BE_UINT32(rect + 4, e._height);
165 				dest = new Common::MemoryReadStream(rect, 2 * sizeof(uint32), DisposeAfterUse::YES);
166 			}
167 
168 			f.close();
169 			return dest;
170 		}
171 	}
172 
173 	return nullptr;
174 }
175 
loadPalette(Common::File & f,const Entry & e,Common::Array<byte> & palette) const176 void Pics::loadPalette(Common::File &f, const Entry &e, Common::Array<byte> &palette) const {
177 	if (e._paletteOffset) {
178 		// Read in the image's palette
179 		assert(e._paletteOffset);
180 		f.seek(e._paletteOffset);
181 		_palette->resize(f.readByte() * 3);
182 		f.read(&(*_palette)[0], _palette->size());
183 	}
184 
185 	if (e._flags & 1) {
186 		byte *entry = &palette[(e._flags >> 12) * 3];
187 		Common::fill(entry, entry + 3, 0);
188 	}
189 }
190 
191 } // End of namespace ZCode
192 } // End of namespace Glk
193