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