]> git.lizzy.rs Git - dragonfireclient.git/blob - src/util/serialize.cpp
Android: bypass broken wide_to_utf8 with wide_to_narrow
[dragonfireclient.git] / src / util / serialize.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "serialize.h"
21 #include "pointer.h"
22 #include "porting.h"
23 #include "util/string.h"
24 #include "../exceptions.h"
25 #include "../irrlichttypes.h"
26
27 #include <sstream>
28 #include <iomanip>
29 #include <vector>
30
31 // Creates a string with the length as the first two bytes
32 std::string serializeString(const std::string &plain)
33 {
34         if(plain.size() > 65535)
35                 throw SerializationError("String too long for serializeString");
36         char buf[2];
37         writeU16((u8*)&buf[0], plain.size());
38         std::string s;
39         s.append(buf, 2);
40         s.append(plain);
41         return s;
42 }
43
44 // Creates a string with the length as the first two bytes from wide string
45 std::string serializeWideString(const std::wstring &plain)
46 {
47         if(plain.size() > 65535)
48                 throw SerializationError("String too long for serializeString");
49         char buf[2];
50         writeU16((u8*)buf, plain.size());
51         std::string s;
52         s.append(buf, 2);
53         for(u32 i=0; i<plain.size(); i++)
54         {
55                 writeU16((u8*)buf, plain[i]);
56                 s.append(buf, 2);
57         }
58         return s;
59 }
60
61 // Reads a string with the length as the first two bytes
62 std::string deSerializeString(std::istream &is)
63 {
64         char buf[2];
65         is.read(buf, 2);
66         if(is.gcount() != 2)
67                 throw SerializationError("deSerializeString: size not read");
68         u16 s_size = readU16((u8*)buf);
69         std::string s;
70         if(s_size == 0)
71                 return s;
72         Buffer<char> buf2(s_size);
73         is.read(&buf2[0], s_size);
74         s.reserve(s_size);
75         s.append(&buf2[0], s_size);
76         return s;
77 }
78
79 // Reads a wide string with the length as the first two bytes
80 std::wstring deSerializeWideString(std::istream &is)
81 {
82         char buf[2];
83         is.read(buf, 2);
84         if(is.gcount() != 2)
85                 throw SerializationError("deSerializeString: size not read");
86         u16 s_size = readU16((u8*)buf);
87         std::wstring s;
88         if(s_size == 0)
89                 return s;
90         s.reserve(s_size);
91         for(u32 i=0; i<s_size; i++)
92         {
93                 is.read(&buf[0], 2);
94                 wchar_t c16 = readU16((u8*)buf);
95                 s.append(&c16, 1);
96         }
97         return s;
98 }
99
100 // Creates a string with the length as the first four bytes
101 std::string serializeLongString(const std::string &plain)
102 {
103         char buf[4];
104         writeU32((u8*)&buf[0], plain.size());
105         std::string s;
106         s.append(buf, 4);
107         s.append(plain);
108         return s;
109 }
110
111 // Reads a string with the length as the first four bytes
112 std::string deSerializeLongString(std::istream &is)
113 {
114         char buf[4];
115         is.read(buf, 4);
116         if(is.gcount() != 4)
117                 throw SerializationError("deSerializeLongString: size not read");
118         u32 s_size = readU32((u8*)buf);
119         std::string s;
120         if(s_size == 0)
121                 return s;
122         Buffer<char> buf2(s_size);
123         is.read(&buf2[0], s_size);
124         s.reserve(s_size);
125         s.append(&buf2[0], s_size);
126         return s;
127 }
128
129 // Creates a string encoded in JSON format (almost equivalent to a C string literal)
130 std::string serializeJsonString(const std::string &plain)
131 {
132         std::ostringstream os(std::ios::binary);
133         os<<"\"";
134         for(size_t i = 0; i < plain.size(); i++)
135         {
136                 char c = plain[i];
137                 switch(c)
138                 {
139                         case '"': os<<"\\\""; break;
140                         case '\\': os<<"\\\\"; break;
141                         case '/': os<<"\\/"; break;
142                         case '\b': os<<"\\b"; break;
143                         case '\f': os<<"\\f"; break;
144                         case '\n': os<<"\\n"; break;
145                         case '\r': os<<"\\r"; break;
146                         case '\t': os<<"\\t"; break;
147                         default:
148                         {
149                                 if(c >= 32 && c <= 126)
150                                 {
151                                         os<<c;
152                                 }
153                                 else
154                                 {
155                                         u32 cnum = (u32) (u8) c;
156                                         os<<"\\u"<<std::hex<<std::setw(4)<<std::setfill('0')<<cnum;
157                                 }
158                                 break;
159                         }
160                 }
161         }
162         os<<"\"";
163         return os.str();
164 }
165
166 // Reads a string encoded in JSON format
167 std::string deSerializeJsonString(std::istream &is)
168 {
169         std::ostringstream os(std::ios::binary);
170         char c, c2;
171
172         // Parse initial doublequote
173         is >> c;
174         if(c != '"')
175                 throw SerializationError("JSON string must start with doublequote");
176
177         // Parse characters
178         for(;;)
179         {
180                 c = is.get();
181                 if(is.eof())
182                         throw SerializationError("JSON string ended prematurely");
183                 if(c == '"')
184                 {
185                         return os.str();
186                 }
187                 else if(c == '\\')
188                 {
189                         c2 = is.get();
190                         if(is.eof())
191                                 throw SerializationError("JSON string ended prematurely");
192                         switch(c2)
193                         {
194                                 default:  os<<c2; break;
195                                 case 'b': os<<'\b'; break;
196                                 case 'f': os<<'\f'; break;
197                                 case 'n': os<<'\n'; break;
198                                 case 'r': os<<'\r'; break;
199                                 case 't': os<<'\t'; break;
200                                 case 'u':
201                                 {
202                                         char hexdigits[4+1];
203                                         is.read(hexdigits, 4);
204                                         if(is.eof())
205                                                 throw SerializationError("JSON string ended prematurely");
206                                         hexdigits[4] = 0;
207                                         std::istringstream tmp_is(hexdigits, std::ios::binary);
208                                         int hexnumber;
209                                         tmp_is >> std::hex >> hexnumber;
210                                         os<<((char)hexnumber);
211                                         break;
212                                 }
213                         }
214                 }
215                 else
216                 {
217                         os<<c;
218                 }
219         }
220         return os.str();
221 }
222
223
224 bool deSerializeStringToStruct(std::string valstr,
225         std::string format, void *out, size_t olen)
226 {
227         size_t len = olen;
228         std::vector<std::string *> strs_alloced;
229         std::string *str;
230         char *f, *snext;
231         size_t pos;
232
233         char *s = &valstr[0];
234         char *buf = new char[len];
235         char *bufpos = buf;
236
237         char *fmtpos, *fmt = &format[0];
238         while ((f = strtok_r(fmt, ",", &fmtpos)) && s) {
239                 fmt = NULL;
240
241                 bool is_unsigned = false;
242                 int width = 0;
243                 char valtype = *f;
244
245                 width = (int)strtol(f + 1, &f, 10);
246                 if (width && valtype == 's')
247                         valtype = 'i';
248
249                 switch (valtype) {
250                         case 'u':
251                                 is_unsigned = true;
252                                 /* FALLTHROUGH */
253                         case 'i':
254                                 if (width == 16) {
255                                         bufpos += PADDING(bufpos, u16);
256                                         if ((bufpos - buf) + sizeof(u16) <= len) {
257                                                 if (is_unsigned)
258                                                         *(u16 *)bufpos = (u16)strtoul(s, &s, 10);
259                                                 else
260                                                         *(s16 *)bufpos = (s16)strtol(s, &s, 10);
261                                         }
262                                         bufpos += sizeof(u16);
263                                 } else if (width == 32) {
264                                         bufpos += PADDING(bufpos, u32);
265                                         if ((bufpos - buf) + sizeof(u32) <= len) {
266                                                 if (is_unsigned)
267                                                         *(u32 *)bufpos = (u32)strtoul(s, &s, 10);
268                                                 else
269                                                         *(s32 *)bufpos = (s32)strtol(s, &s, 10);
270                                         }
271                                         bufpos += sizeof(u32);
272                                 } else if (width == 64) {
273                                         bufpos += PADDING(bufpos, u64);
274                                         if ((bufpos - buf) + sizeof(u64) <= len) {
275                                                 if (is_unsigned)
276                                                         *(u64 *)bufpos = (u64)strtoull(s, &s, 10);
277                                                 else
278                                                         *(s64 *)bufpos = (s64)strtoll(s, &s, 10);
279                                         }
280                                         bufpos += sizeof(u64);
281                                 }
282                                 s = strchr(s, ',');
283                                 break;
284                         case 'b':
285                                 snext = strchr(s, ',');
286                                 if (snext)
287                                         *snext++ = 0;
288
289                                 bufpos += PADDING(bufpos, bool);
290                                 if ((bufpos - buf) + sizeof(bool) <= len)
291                                         *(bool *)bufpos = is_yes(std::string(s));
292                                 bufpos += sizeof(bool);
293
294                                 s = snext;
295                                 break;
296                         case 'f':
297                                 bufpos += PADDING(bufpos, float);
298                                 if ((bufpos - buf) + sizeof(float) <= len)
299                                         *(float *)bufpos = strtof(s, &s);
300                                 bufpos += sizeof(float);
301
302                                 s = strchr(s, ',');
303                                 break;
304                         case 's':
305                                 while (*s == ' ' || *s == '\t')
306                                         s++;
307                                 if (*s++ != '"') //error, expected string
308                                         goto fail;
309                                 snext = s;
310
311                                 while (snext[0] && !(snext[-1] != '\\' && snext[0] == '"'))
312                                         snext++;
313                                 *snext++ = 0;
314
315                                 bufpos += PADDING(bufpos, std::string *);
316
317                                 str = new std::string(s);
318                                 pos = 0;
319                                 while ((pos = str->find("\\\"", pos)) != std::string::npos)
320                                         str->erase(pos, 1);
321
322                                 if ((bufpos - buf) + sizeof(std::string *) <= len)
323                                         *(std::string **)bufpos = str;
324                                 bufpos += sizeof(std::string *);
325                                 strs_alloced.push_back(str);
326
327                                 s = *snext ? snext + 1 : NULL;
328                                 break;
329                         case 'v':
330                                 while (*s == ' ' || *s == '\t')
331                                         s++;
332                                 if (*s++ != '(') //error, expected vector
333                                         goto fail;
334
335                                 if (width == 2) {
336                                         bufpos += PADDING(bufpos, v2f);
337
338                                         if ((bufpos - buf) + sizeof(v2f) <= len) {
339                                         v2f *v = (v2f *)bufpos;
340                                                 v->X = strtof(s, &s);
341                                                 s++;
342                                                 v->Y = strtof(s, &s);
343                                         }
344
345                                         bufpos += sizeof(v2f);
346                                 } else if (width == 3) {
347                                         bufpos += PADDING(bufpos, v3f);
348                                         if ((bufpos - buf) + sizeof(v3f) <= len) {
349                                                 v3f *v = (v3f *)bufpos;
350                                                 v->X = strtof(s, &s);
351                                                 s++;
352                                                 v->Y = strtof(s, &s);
353                                                 s++;
354                                                 v->Z = strtof(s, &s);
355                                         }
356
357                                         bufpos += sizeof(v3f);
358                                 }
359                                 s = strchr(s, ',');
360                                 break;
361                         default: //error, invalid format specifier
362                                 goto fail;
363                 }
364
365                 if (s && *s == ',')
366                         s++;
367
368                 if ((size_t)(bufpos - buf) > len) //error, buffer too small
369                         goto fail;
370         }
371
372         if (f && *f) { //error, mismatched number of fields and values
373 fail:
374                 for (size_t i = 0; i != strs_alloced.size(); i++)
375                         delete strs_alloced[i];
376                 delete[] buf;
377                 return false;
378         }
379
380         memcpy(out, buf, olen);
381         delete[] buf;
382         return true;
383 }
384
385
386 // Casts *buf to a signed or unsigned fixed-width integer of 'w' width
387 #define SIGN_CAST(w, buf) (is_unsigned ? *((u##w *) buf) : *((s##w *) buf))
388
389 bool serializeStructToString(std::string *out,
390         std::string format, void *value)
391 {
392         std::ostringstream os;
393         std::string str;
394         char *f;
395         size_t strpos;
396
397         char *bufpos = (char *) value;
398         char *fmtpos, *fmt = &format[0];
399         while ((f = strtok_r(fmt, ",", &fmtpos))) {
400                 fmt = NULL;
401                 bool is_unsigned = false;
402                 int width = 0;
403                 char valtype = *f;
404
405                 width = (int)strtol(f + 1, &f, 10);
406                 if (width && valtype == 's')
407                         valtype = 'i';
408
409                 switch (valtype) {
410                         case 'u':
411                                 is_unsigned = true;
412                                 /* FALLTHROUGH */
413                         case 'i':
414                                 if (width == 16) {
415                                         bufpos += PADDING(bufpos, u16);
416                                         os << SIGN_CAST(16, bufpos);
417                                         bufpos += sizeof(u16);
418                                 } else if (width == 32) {
419                                         bufpos += PADDING(bufpos, u32);
420                                         os << SIGN_CAST(32, bufpos);
421                                         bufpos += sizeof(u32);
422                                 } else if (width == 64) {
423                                         bufpos += PADDING(bufpos, u64);
424                                         os << SIGN_CAST(64, bufpos);
425                                         bufpos += sizeof(u64);
426                                 }
427                                 break;
428                         case 'b':
429                                 bufpos += PADDING(bufpos, bool);
430                                 os << std::boolalpha << *((bool *) bufpos);
431                                 bufpos += sizeof(bool);
432                                 break;
433                         case 'f':
434                                 bufpos += PADDING(bufpos, float);
435                                 os << *((float *) bufpos);
436                                 bufpos += sizeof(float);
437                                 break;
438                         case 's':
439                                 bufpos += PADDING(bufpos, std::string *);
440                                 str = **((std::string **) bufpos);
441
442                                 strpos = 0;
443                                 while ((strpos = str.find('"', strpos)) != std::string::npos) {
444                                         str.insert(strpos, 1, '\\');
445                                         strpos += 2;
446                                 }
447
448                                 os << str;
449                                 bufpos += sizeof(std::string *);
450                                 break;
451                         case 'v':
452                                 if (width == 2) {
453                                         bufpos += PADDING(bufpos, v2f);
454                                         v2f *v = (v2f *) bufpos;
455                                         os << '(' << v->X << ", " << v->Y << ')';
456                                         bufpos += sizeof(v2f);
457                                 } else {
458                                         bufpos += PADDING(bufpos, v3f);
459                                         v3f *v = (v3f *) bufpos;
460                                         os << '(' << v->X << ", " << v->Y << ", " << v->Z << ')';
461                                         bufpos += sizeof(v3f);
462                                 }
463                                 break;
464                         default:
465                                 return false;
466                 }
467                 os << ", ";
468         }
469         *out = os.str();
470
471         // Trim off the trailing comma and space
472         if (out->size() >= 2) {
473                 out->resize(out->size() - 2);
474         }
475
476         return true;
477 }
478
479 #undef SIGN_CAST