CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Common/Data/Format/ZIMLoad.cpp
Views: 1401
1
#include <cstring>
2
#include <cstdio>
3
#include <cstdlib>
4
#include <zstd.h>
5
6
#include "zlib.h"
7
8
#include "Common/Log.h"
9
#include "Common/Data/Format/ZIMLoad.h"
10
#include "Common/Math/math_util.h"
11
#include "Common/File/VFS/VFS.h"
12
13
int ezuncompress(unsigned char* pDest, long* pnDestLen, const unsigned char* pSrc, long nSrcLen) {
14
z_stream stream;
15
stream.next_in = (Bytef*)pSrc;
16
stream.avail_in = (uInt)nSrcLen;
17
/* Check for source > 64K on 16-bit machine: */
18
if ((uLong)stream.avail_in != (uLong)nSrcLen) return Z_BUF_ERROR;
19
20
uInt destlen = (uInt)*pnDestLen;
21
if ((uLong)destlen != (uLong)*pnDestLen) return Z_BUF_ERROR;
22
stream.zalloc = (alloc_func)0;
23
stream.zfree = (free_func)0;
24
25
int err = inflateInit(&stream);
26
if (err != Z_OK) return err;
27
28
int nExtraChunks = 0;
29
do {
30
stream.next_out = pDest;
31
stream.avail_out = destlen;
32
err = inflate(&stream, Z_FINISH);
33
if (err == Z_STREAM_END )
34
break;
35
if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
36
err = Z_DATA_ERROR;
37
if (err != Z_BUF_ERROR) {
38
inflateEnd(&stream);
39
return err;
40
}
41
nExtraChunks += 1;
42
} while (stream.avail_out == 0);
43
44
*pnDestLen = stream.total_out;
45
46
err = inflateEnd(&stream);
47
if (err != Z_OK) return err;
48
49
return nExtraChunks ? Z_BUF_ERROR : Z_OK;
50
}
51
52
int LoadZIMPtr(const uint8_t *zim, size_t datasize, int *width, int *height, int *flags, uint8_t **image) {
53
if (zim[0] != 'Z' || zim[1] != 'I' || zim[2] != 'M' || zim[3] != 'G') {
54
ERROR_LOG(Log::IO, "Not a ZIM file");
55
return 0;
56
}
57
memcpy(width, zim + 4, 4);
58
memcpy(height, zim + 8, 4);
59
memcpy(flags, zim + 12, 4);
60
61
int num_levels = 1;
62
int image_data_size[ZIM_MAX_MIP_LEVELS];
63
if (*flags & ZIM_HAS_MIPS) {
64
num_levels = log2i(*width < *height ? *width : *height) + 1;
65
}
66
int total_data_size = 0;
67
for (int i = 0; i < num_levels; i++) {
68
if (i > 0) {
69
width[i] = width[i-1] / 2;
70
height[i] = height[i-1] / 2;
71
}
72
switch (*flags & ZIM_FORMAT_MASK) {
73
case ZIM_RGBA8888:
74
image_data_size[i] = width[i] * height[i] * 4;
75
break;
76
case ZIM_RGBA4444:
77
case ZIM_RGB565:
78
image_data_size[i] = width[i] * height[i] * 2;
79
break;
80
default:
81
ERROR_LOG(Log::IO, "Invalid ZIM format %i", *flags & ZIM_FORMAT_MASK);
82
return 0;
83
}
84
total_data_size += image_data_size[i];
85
}
86
87
if (total_data_size == 0) {
88
ERROR_LOG(Log::IO, "Invalid ZIM data size 0");
89
return 0;
90
}
91
92
image[0] = (uint8_t *)malloc(total_data_size);
93
for (int i = 1; i < num_levels; i++) {
94
image[i] = image[i-1] + image_data_size[i-1];
95
}
96
97
if (*flags & ZIM_ZLIB_COMPRESSED) {
98
long outlen = (long)total_data_size;
99
int retcode = ezuncompress(*image, &outlen, (unsigned char *)(zim + 16), (long)datasize - 16);
100
if (Z_OK != retcode) {
101
ERROR_LOG(Log::IO, "ZIM zlib format decompression failed: %d", retcode);
102
free(*image);
103
*image = 0;
104
return 0;
105
}
106
if (outlen != total_data_size) {
107
// Shouldn't happen if return value was Z_OK.
108
ERROR_LOG(Log::IO, "Wrong size data in ZIM: %i vs %i", (int)outlen, (int)total_data_size);
109
}
110
} else if (*flags & ZIM_ZSTD_COMPRESSED) {
111
size_t outlen = ZSTD_decompress(*image, total_data_size, zim + 16, datasize - 16);
112
if (outlen != (size_t)total_data_size) {
113
ERROR_LOG(Log::IO, "ZIM zstd format decompression failed: %lld", (long long)outlen);
114
free(*image);
115
*image = 0;
116
return 0;
117
}
118
} else {
119
memcpy(*image, zim + 16, datasize - 16);
120
if (datasize - 16 != (size_t)total_data_size) {
121
ERROR_LOG(Log::IO, "Wrong size data in ZIM: %i vs %i", (int)(datasize-16), (int)total_data_size);
122
}
123
}
124
return num_levels;
125
}
126
127
int LoadZIM(const char *filename, int *width, int *height, int *format, uint8_t **image) {
128
size_t size;
129
uint8_t *buffer = g_VFS.ReadFile(filename, &size);
130
if (!buffer) {
131
ERROR_LOG(Log::IO, "Couldn't read data for '%s'", filename);
132
return 0;
133
}
134
135
int retval = LoadZIMPtr(buffer, size, width, height, format, image);
136
if (!retval) {
137
ERROR_LOG(Log::IO, "Not a valid ZIM file: %s (size: %lld bytes)", filename, (long long)size);
138
}
139
delete [] buffer;
140
return retval;
141
}
142
143