1/*
2 * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#include "filestream.h"
24#include "binarytree.h"
25#include <framework/core/application.h>
26
27#define PHYSFS_DEPRECATED
28#include <physfs.h>
29#include <zlib.h>
30
31FileStream::FileStream(const std::string& name, PHYSFS_File *fileHandle, bool writeable) :
32 m_name(name),
33 m_fileHandle(fileHandle),
34 m_pos(0),
35 m_writeable(writeable),
36 m_caching(false)
37{
38}
39
40FileStream::FileStream(const std::string& name, std::string&& buffer) :
41 m_name(name),
42 m_fileHandle(nullptr),
43 m_pos(0),
44 m_writeable(false),
45 m_caching(true)
46{
47 if (!initFromGzip(buffer)) {
48 m_strData = std::move(buffer);
49 }
50}
51
52bool FileStream::initFromGzip(const std::string& buffer)
53{
54 if (buffer.size() < 10 || (uint8_t)buffer[0] != 0x1f ||
55 (uint8_t)buffer[1] != 0x8b || (uint8_t)buffer[2] != 0x08) {
56 return false;
57 }
58
59 uint32_t fileSize = *(uint32_t*)(&buffer[buffer.size() - 4]);
60 if (fileSize > 512 * 1024 * 1024 || fileSize * 2 < buffer.size()) {
61 return false;
62 }
63
64 z_stream stream;
65 memset(&stream, 0, sizeof(stream));
66
67 stream.next_in = (Bytef*)buffer.c_str();
68 stream.avail_in = buffer.size();
69 if (inflateInit2(&stream, 15 + 32) != Z_OK)
70 return false;
71
72 m_data.grow(fileSize, true);
73 stream.next_out = &m_data[0];
74 stream.avail_out = m_data.size();
75
76 inflate(&stream, Z_SYNC_FLUSH);
77
78 inflateEnd(&stream);
79 return true;
80}
81
82
83FileStream::~FileStream()
84{
85 VALIDATE(!g_app.isTerminated());
86 if(!g_app.isTerminated())
87 close();
88}
89
90void FileStream::close()
91{
92 if(m_fileHandle && PHYSFS_isInit()) {
93 if(!PHYSFS_close(m_fileHandle))
94 throwError("close failed", true);
95 m_fileHandle = nullptr;
96 }
97
98 m_data.clear();
99 m_strData.clear();
100 m_pos = 0;
101}
102
103void FileStream::flush()
104{
105 if(!m_writeable)
106 throwError("filestream is not writeable");
107
108 if(m_fileHandle) {
109 if(m_caching) {
110 if(!PHYSFS_seek(m_fileHandle, 0))
111 throwError("flush seek failed", true);
112 uint len = m_data.size();
113 if(PHYSFS_writeBytes(m_fileHandle, m_data.data(), len) != len)
114 throwError("flush write failed", true);
115 }
116
117 if(PHYSFS_flush(m_fileHandle) == 0)
118 throwError("flush failed", true);
119 }
120}
121
122int FileStream::read(void* buffer, uint32 size, uint32 nmemb)
123{
124 if (!m_caching) {
125 int res = PHYSFS_readBytes(m_fileHandle, buffer, size * nmemb);
126 if (res == -1)
127 throwError("read failed", true);
128 return res;
129 } if (!m_strData.empty()) {
130 int writePos = 0;
131 uint8* outBuffer = (uint8*)buffer;
132 for (uint i = 0; i < nmemb; ++i) {
133 if (m_pos + size > m_strData.size())
134 return i;
135
136 for (uint j = 0; j < size; ++j)
137 outBuffer[writePos++] = m_strData[m_pos++];
138 }
139 return nmemb;
140 } else {
141 int writePos = 0;
142 uint8 *outBuffer = (uint8*)buffer;
143 for(uint i=0;i<nmemb;++i) {
144 if(m_pos+size > m_data.size())
145 return i;
146
147 for(uint j=0;j<size;++j)
148 outBuffer[writePos++] = m_data[m_pos++];
149 }
150 return nmemb;
151 }
152}
153
154void FileStream::write(const void *buffer, uint32 count)
155{
156 if(!m_caching) {
157 if(PHYSFS_writeBytes(m_fileHandle, buffer, count) != count)
158 throwError("write failed", true);
159 } else {
160 m_data.grow(m_pos + count);
161 memcpy(&m_data[m_pos], buffer, count);
162 m_pos += count;
163 }
164}
165
166void FileStream::seek(uint32 pos)
167{
168 if (!m_caching) {
169 if (!PHYSFS_seek(m_fileHandle, pos))
170 throwError("seek failed", true);
171 } else if(!m_strData.empty()) {
172 if (pos > m_strData.size())
173 throwError("seek failed");
174 m_pos = pos;
175 } else {
176 if(pos > m_data.size())
177 throwError("seek failed");
178 m_pos = pos;
179 }
180}
181
182void FileStream::skip(uint len)
183{
184 seek(tell() + len);
185}
186
187uint FileStream::size()
188{
189 if (!m_caching)
190 return PHYSFS_fileLength(m_fileHandle);
191 else if (!m_strData.empty())
192 return m_strData.size();
193 else
194 return m_data.size();
195}
196
197uint FileStream::tell()
198{
199 if(!m_caching)
200 return PHYSFS_tell(m_fileHandle);
201 else
202 return m_pos;
203}
204
205bool FileStream::eof()
206{
207 if(!m_caching)
208 return PHYSFS_eof(m_fileHandle);
209 else if (!m_strData.empty())
210 return m_pos >= m_strData.size();
211 else
212 return m_pos >= m_data.size();
213}
214
215uint8 FileStream::getU8()
216{
217 uint8 v = 0;
218 if(!m_caching) {
219 if(PHYSFS_readBytes(m_fileHandle, &v, 1) != 1)
220 throwError("read failed", true);
221 } else if (!m_strData.empty()) {
222 if (m_pos + 1 > m_strData.size())
223 throwError("read failed");
224
225 v = m_strData[m_pos];
226 m_pos += 1;
227 } else {
228 if(m_pos+1 > m_data.size())
229 throwError("read failed");
230
231 v = m_data[m_pos];
232 m_pos += 1;
233 }
234 return v;
235}
236
237uint16 FileStream::getU16()
238{
239 uint16 v = 0;
240 if(!m_caching) {
241 if(PHYSFS_readULE16(m_fileHandle, &v) == 0)
242 throwError("read failed", true);
243 } else if(!m_strData.empty()) {
244 if (m_pos + 2 > m_strData.size())
245 throwError("read failed");
246
247 v = stdext::readULE16((const uint8_t*)&m_strData[m_pos]);
248 m_pos += 2;
249 } else {
250 if(m_pos+2 > m_data.size())
251 throwError("read failed");
252
253 v = stdext::readULE16(&m_data[m_pos]);
254 m_pos += 2;
255 }
256 return v;
257}
258
259uint32 FileStream::getU32()
260{
261 uint32 v = 0;
262 if(!m_caching) {
263 if(PHYSFS_readULE32(m_fileHandle, &v) == 0)
264 throwError("read failed", true);
265 } else if(!m_strData.empty()) {
266 if (m_pos + 4 > m_strData.size())
267 throwError("read failed");
268
269 v = stdext::readULE32((const uint8_t*)&m_strData[m_pos]);
270 m_pos += 4;
271 } else {
272 if (m_pos + 4 > m_data.size())
273 throwError("read failed");
274
275 v = stdext::readULE32(&m_data[m_pos]);
276 m_pos += 4;
277 }
278 return v;
279}
280
281uint64 FileStream::getU64()
282{
283 uint64 v = 0;
284 if(!m_caching) {
285 if(PHYSFS_readULE64(m_fileHandle, (PHYSFS_uint64*)&v) == 0)
286 throwError("read failed", true);
287 } else if(!m_strData.empty()) {
288 if (m_pos + 8 > m_strData.size())
289 throwError("read failed");
290 v = stdext::readULE64((const uint8_t*)&m_strData[m_pos]);
291 m_pos += 8;
292 } else {
293 if (m_pos + 8 > m_data.size())
294 throwError("read failed");
295 v = stdext::readULE64(&m_data[m_pos]);
296 m_pos += 8;
297 }
298 return v;
299}
300
301int8 FileStream::get8()
302{
303 int8 v = 0;
304 if(!m_caching) {
305 if(PHYSFS_readBytes(m_fileHandle, &v, 1) != 1)
306 throwError("read failed", true);
307 } else if(!m_strData.empty()) {
308 if (m_pos + 1 > m_strData.size())
309 throwError("read failed");
310
311 v = m_strData[m_pos];
312 m_pos += 1;
313 } else {
314 if(m_pos+1 > m_data.size())
315 throwError("read failed");
316
317 v = m_data[m_pos];
318 m_pos += 1;
319 }
320 return v;
321}
322
323int16 FileStream::get16()
324{
325 int16 v = 0;
326 if(!m_caching) {
327 if(PHYSFS_readSLE16(m_fileHandle, &v) == 0)
328 throwError("read failed", true);
329 } else if(!m_strData.empty()) {
330 if (m_pos + 2 > m_strData.size())
331 throwError("read failed");
332
333 v = stdext::readSLE16((const uint8_t*)&m_strData[m_pos]);
334 m_pos += 2;
335 } else {
336 if (m_pos + 2 > m_data.size())
337 throwError("read failed");
338
339 v = stdext::readSLE16(&m_data[m_pos]);
340 m_pos += 2;
341 }
342 return v;
343}
344
345int32 FileStream::get32()
346{
347 int32 v = 0;
348 if(!m_caching) {
349 if(PHYSFS_readSLE32(m_fileHandle, &v) == 0)
350 throwError("read failed", true);
351 } else if(!m_strData.empty()) {
352 if (m_pos + 4 > m_strData.size())
353 throwError("read failed");
354
355 v = stdext::readSLE32((const uint8_t*)&m_strData[m_pos]);
356 m_pos += 4;
357 } else {
358 if (m_pos + 4 > m_data.size())
359 throwError("read failed");
360
361 v = stdext::readSLE32(&m_data[m_pos]);
362 m_pos += 4;
363 }
364 return v;
365}
366
367int64 FileStream::get64()
368{
369 int64 v = 0;
370 if(!m_caching) {
371 if(PHYSFS_readSLE64(m_fileHandle, (PHYSFS_sint64*)&v) == 0)
372 throwError("read failed", true);
373 } else if(!m_strData.empty()) {
374 if (m_pos + 8 > m_strData.size())
375 throwError("read failed");
376 v = stdext::readSLE64((const uint8_t*)&m_strData[m_pos]);
377 m_pos += 8;
378 } else {
379 if (m_pos + 8 > m_data.size())
380 throwError("read failed");
381 v = stdext::readSLE64(&m_data[m_pos]);
382 m_pos += 8;
383 }
384 return v;
385}
386
387std::string FileStream::getString()
388{
389 std::string str;
390 uint16 len = getU16();
391 if (len > 0) {
392 std::vector<uint8_t> buffer(len, 0);
393 if (m_fileHandle) {
394 if (PHYSFS_read(m_fileHandle, &buffer[0], 1, len) == 0)
395 throwError("read failed", true);
396 else
397 str = std::string(buffer.begin(), buffer.end());
398 } else if(!m_strData.empty()) {
399 if (m_pos + len > m_strData.size()) {
400 throwError("read failed");
401 return 0;
402 }
403
404 str = std::string((char*)&m_strData[m_pos], len);
405 m_pos += len;
406 } else {
407 if (m_pos + len > m_data.size()) {
408 throwError("read failed");
409 return 0;
410 }
411
412 str = std::string((char*)&m_data[m_pos], len);
413 m_pos += len;
414 }
415 } else if (len != 0)
416 throwError("read failed because string is too big");
417 return str;
418}
419
420BinaryTreePtr FileStream::getBinaryTree()
421{
422 uint8 byte = getU8();
423 if(byte != BINARYTREE_NODE_START)
424 stdext::throw_exception(stdext::format("failed to read node start (getBinaryTree): %d", byte));
425
426 return BinaryTreePtr(new BinaryTree(asFileStream()));
427}
428
429void FileStream::startNode(uint8 n)
430{
431 addU8(BINARYTREE_NODE_START);
432 addU8(n);
433}
434
435void FileStream::endNode()
436{
437 addU8(BINARYTREE_NODE_END);
438}
439
440void FileStream::addU8(uint8 v)
441{
442 if(!m_caching) {
443 if(PHYSFS_writeBytes(m_fileHandle, &v, 1) != 1)
444 throwError("write failed", true);
445 } else {
446 m_data.add(v);
447 m_pos++;
448 }
449}
450
451void FileStream::addU16(uint16 v)
452{
453 if(!m_caching) {
454 if(PHYSFS_writeULE16(m_fileHandle, v) == 0)
455 throwError("write failed", true);
456 } else {
457 m_data.grow(m_pos + 2);
458 stdext::writeULE16(&m_data[m_pos], v);
459 m_pos += 2;
460 }
461}
462
463void FileStream::addU32(uint32 v)
464{
465 if(!m_caching) {
466 if(PHYSFS_writeULE32(m_fileHandle, v) == 0)
467 throwError("write failed", true);
468 } else {
469 m_data.grow(m_pos + 4);
470 stdext::writeULE32(&m_data[m_pos], v);
471 m_pos += 4;
472 }
473}
474
475void FileStream::addU64(uint64 v)
476{
477 if(!m_caching) {
478 if(PHYSFS_writeULE64(m_fileHandle, v) == 0)
479 throwError("write failed", true);
480 } else {
481 m_data.grow(m_pos + 8);
482 stdext::writeULE64(&m_data[m_pos], v);
483 m_pos += 8;
484 }
485}
486
487void FileStream::add8(int8 v)
488{
489 if(!m_caching) {
490 if(PHYSFS_writeBytes(m_fileHandle, &v, 1) != 1)
491 throwError("write failed", true);
492 } else {
493 m_data.add(v);
494 m_pos++;
495 }
496}
497
498void FileStream::add16(int16 v)
499{
500 if(!m_caching) {
501 if(PHYSFS_writeSLE16(m_fileHandle, v) == 0)
502 throwError("write failed", true);
503 } else {
504 m_data.grow(m_pos + 2);
505 stdext::writeSLE16(&m_data[m_pos], v);
506 m_pos += 2;
507 }
508}
509
510void FileStream::add32(int32 v)
511{
512 if(!m_caching) {
513 if(PHYSFS_writeSLE32(m_fileHandle, v) == 0)
514 throwError("write failed", true);
515 } else {
516 m_data.grow(m_pos + 4);
517 stdext::writeSLE32(&m_data[m_pos], v);
518 m_pos += 4;
519 }
520}
521
522void FileStream::add64(int64 v)
523{
524 if(!m_caching) {
525 if(PHYSFS_writeSLE64(m_fileHandle, v) == 0)
526 throwError("write failed", true);
527 } else {
528 m_data.grow(m_pos + 8);
529 stdext::writeSLE64(&m_data[m_pos], v);
530 m_pos += 8;
531 }
532}
533
534void FileStream::addString(const std::string& v)
535{
536 addU16(v.length());
537 write(v.c_str(), v.length());
538}
539
540void FileStream::throwError(const std::string& message, bool physfsError)
541{
542 std::string completeMessage = stdext::format("in file '%s': %s", m_name, message);
543 if(physfsError)
544 completeMessage += std::string(": ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode());
545 stdext::throw_exception(completeMessage);
546}
547
548