1 /*
2  * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/>
3  *           (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com>
4  *
5  * This file is part of lsp-plugins
6  * Created on: 6 сент. 2019 г.
7  *
8  * lsp-plugins is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * any later version.
12  *
13  * lsp-plugins is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <core/debug.h>
23 #include <core/io/InFileStream.h>
24 #include <core/io/OutMemoryStream.h>
25 #include <core/io/InMemoryStream.h>
26 #include <core/io/InSequence.h>
27 
28 #include <core/files/RoomEQWizard.h>
29 #include <core/files/java/ObjectStream.h>
30 
31 #include <dsp/endian.h>
32 
33 #include <data/cstorage.h>
34 
35 namespace lsp
36 {
37     namespace room_ew
38     {
39         const char *charsets[] =
40         {
41             "UTF-8",
42             "UTF-16LE",
43             "UTF-16BE",
44             NULL
45         };
46 
build_config(const LSPString * eq,const LSPString * notes,int32_t major,int32_t minor,size_t filters)47         config_t *build_config(const LSPString *eq, const LSPString *notes,
48                 int32_t major, int32_t minor, size_t filters)
49         {
50             const char *peq     = eq->get_utf8();
51             if (peq == NULL)
52                 return NULL;
53             const char *pnotes  = notes->get_utf8();
54             if (pnotes == NULL)
55                 return NULL;
56 
57             size_t  n_hdr       = ::strlen(peq) + 1;
58             size_t  n_notes     = ::strlen(pnotes) + 1;
59             size_t  n_strings   = ALIGN_SIZE(n_hdr + n_notes, DEFAULT_ALIGN);
60             size_t  n_cfg       = ALIGN_SIZE(sizeof(config_t), DEFAULT_ALIGN);
61             size_t  n_filters   = ALIGN_SIZE(sizeof(filter_t) * filters, DEFAULT_ALIGN);
62             size_t  to_alloc    = n_strings + n_cfg + n_filters;
63 
64             uint8_t *ptr        = reinterpret_cast<uint8_t *>(::malloc(to_alloc));
65             if (ptr == NULL)
66                 return NULL;
67             ::bzero(ptr, to_alloc);
68 
69             config_t *cfg       = reinterpret_cast<config_t *>(ptr);
70             ptr                += n_cfg;
71 
72             char *xeq          = reinterpret_cast<char *>(ptr);
73             char *xnotes        = &xeq[n_hdr];
74             ptr                += n_strings;
75             ::memcpy(xeq, peq, n_hdr);
76             ::memcpy(xnotes, pnotes, n_notes);
77 
78             cfg->vFilters       = reinterpret_cast<filter_t *>(ptr);
79             ptr                += n_filters;
80 
81             // Fill the basic configuration
82             cfg->nVerMaj        = major;
83             cfg->nVerMin        = minor;
84             cfg->sEqType        = xeq;
85             cfg->sNotes         = xnotes;
86             cfg->nFilters       = filters;
87 
88             return cfg;
89         }
90 
decode_filter_type(const char * type)91         filter_type_t decode_filter_type(const char *type)
92         {
93             #define DECODE(text, ftype) \
94                 if (!::strcasecmp(type, text)) return ftype;
95 
96             DECODE("PK", PK);
97             DECODE("MODAL", MODAL);
98             DECODE("LP", LP);
99             DECODE("HP", HP);
100             DECODE("LPQ", LPQ);
101             DECODE("HPQ", HPQ);
102             DECODE("LS", LS);
103             DECODE("HS", HS);
104             DECODE("LS6", LS6);
105             DECODE("HS6", HS6);
106             DECODE("LS12", LS12);
107             DECODE("HS12", HS12);
108             DECODE("NO", NO);
109             DECODE("AP", AP);
110 
111             #undef DECODE
112 
113             return NONE;
114         }
115 
load_object_stream(java::ObjectStream * os,config_t ** dst)116         status_t load_object_stream(java::ObjectStream *os, config_t **dst)
117         {
118             status_t res;
119             LSPString eq, notes, xeq;
120             java::RawArray *filters;
121             java::int_t major=0, minor=0, stub=0;
122 
123             // Read the REW data in Java serialization format
124             if ((res = os->read_string(&eq)) != STATUS_OK)
125                 return res;
126             if (!xeq.set_ascii("Equaliser:"))
127                 return STATUS_NO_MEM;
128             ssize_t idx = eq.index_of(&xeq);
129             if (idx >= 0)
130                 eq.remove(0, idx + xeq.length());
131             lsp_trace("equalizer: %s", eq.get_utf8());
132             if ((res = os->read_int(&major)) != STATUS_OK)
133                 return res;
134             if ((res = os->read_int(&minor)) != STATUS_OK)
135                 return res;
136             lsp_trace("version: %d.%d", int(major), int(minor));
137             if ((res = os->read_string(&notes)) != STATUS_OK)
138                 return res;
139             if (notes.starts_with_ascii("Notes:"))
140                 notes.remove(0, 6);
141             lsp_trace("notes: %s", notes.get_utf8());
142 
143             if ((res = os->read_int(&stub)) != STATUS_OK) // Don't know what this number actually means currently
144                 return res;
145             lsp_trace("stub: %d", int(stub));
146             if ((res = os->read_array(&filters)) != STATUS_OK)
147                 return res;
148 
149             // Now we are ready to allocate the data
150             config_t *cfg   = build_config(&eq, &notes, major, minor, filters->length());
151             if (cfg == NULL)
152                 return STATUS_NO_MEM;
153 
154             // Read the entire data
155             const java::Object **vsf = filters->get_objects();
156             filter_t *vdf = cfg->vFilters;
157 
158             for (size_t i=0, n=filters->length(); i<n; ++i)
159             {
160                 const java::Object *sf  = vsf[i];
161                 filter_t *df            = &vdf[i];
162 
163                 java::double_t Q, fc, gain;
164                 java::bool_t enabled;
165                 const char *ftype;
166 
167                 // Read the filter parameters
168                 if ((res = sf->get_double("Q", &Q)) != STATUS_OK)
169                     break;
170                 if ((res = sf->get_double("fc", &fc)) != STATUS_OK)
171                     break;
172                 if ((res = sf->get_double("gain", &gain)) != STATUS_OK)
173                     break;
174                 if ((res = sf->get_bool("enabled", &enabled)) != STATUS_OK)
175                     break;
176                 if ((res = sf->get_enum("filterType", &ftype)) != STATUS_OK)
177                     break;
178 
179                 lsp_trace(
180                         "Filter %d: %s %s Fc %.0f Hz Gain %.1f dB Q %.7f",
181                         int(i+1), (enabled) ? "ON" : "OFF", ftype,
182                         double(fc), double(gain), double(Q)
183                     );
184 
185 //                lsp_trace(
186 //                        "check_filter(&vf[idx++], %s, room_ew::%s, %.2f, %.2f, %.7f);",
187 //                        (enabled) ? "true" : "false", ftype,
188 //                        double(fc), double(gain), double(Q)
189 //                    );
190 
191                 // Fill the state of filter
192                 df->Q           = Q;
193                 df->fc          = fc;
194                 df->gain        = gain;
195                 df->enabled     = enabled;
196                 df->filterType  = decode_filter_type(ftype);
197             }
198 
199             if ((res != STATUS_OK) || (dst == NULL))
200                 ::free(cfg);
201             else if (res == STATUS_OK)
202                 *dst    = cfg;
203 
204             return STATUS_OK;
205         }
206 
load_java(io::IInStream * is,config_t ** dst)207         status_t load_java(io::IInStream *is, config_t **dst)
208         {
209             // Open file
210             java::Handles handles;
211             java::ObjectStream os(&handles);
212 
213             status_t res = os.wrap(is, WRAP_NONE);
214             if (res == STATUS_OK)
215                 res     = load_object_stream(&os, dst);
216 
217             if (res == STATUS_OK)
218                 return os.close();
219 
220             os.close();
221             return res;
222         }
223 
skip_whitespace(const LSPString * s,size_t * offset)224         status_t skip_whitespace(const LSPString *s, size_t *offset)
225         {
226             size_t len = s->length();
227             while (*offset < len)
228             {
229                 switch (s->char_at(*offset))
230                 {
231                     case ' ':
232                     case '\n':
233                     case '\t':
234                     case '\r':
235                         ++(*offset);
236                         break;
237                     default:
238                         return STATUS_OK;
239                 }
240             }
241 
242             return STATUS_OK;
243         }
244 
skip_data(const LSPString * s,size_t * offset)245         status_t skip_data(const LSPString *s, size_t *offset)
246         {
247             size_t len = s->length();
248             while (*offset < len)
249             {
250                 switch (s->char_at(*offset))
251                 {
252                     case ' ':
253                     case '\n':
254                     case '\t':
255                     case '\r':
256                         return STATUS_OK;
257                     default:
258                         ++(*offset);
259                         break;
260                 }
261             }
262 
263             return STATUS_OK;
264         }
265 
parse_decimal(ssize_t * dst,const LSPString * s,size_t * offset)266         status_t parse_decimal(ssize_t *dst, const LSPString *s, size_t *offset)
267         {
268             status_t res = skip_whitespace(s, offset);
269             if (res != STATUS_OK)
270                 return res;
271 
272             ssize_t num = 0, n=0;
273             size_t len = s->length();
274             for ( ; *offset < len; ++(*offset), ++n)
275             {
276                 lsp_wchar_t wc = s->char_at(*offset);
277                 if ((wc >= '0') && (wc <= '9'))
278                     num = num * 10 + (wc - '0');
279                 else
280                     break;
281             }
282             if (n <= 0)
283                 return STATUS_BAD_FORMAT;
284 
285             *dst    = num;
286             return STATUS_OK;
287         }
288 
parse_double(double * dst,const LSPString * s,size_t * offset)289         status_t parse_double(double *dst, const LSPString *s, size_t *offset)
290         {
291             status_t res = skip_whitespace(s, offset);
292             if (res != STATUS_OK)
293                 return res;
294 
295             lsp_trace("s = %s", s->get_utf8(*offset));
296 
297             size_t len = s->length();
298 
299             // Check sign
300             bool negative = false;
301             ssize_t is = 0;
302             if (*offset < len)
303             {
304                 if (s->char_at(*offset) == '+')
305                 {
306                     ++is;
307                     ++(*offset);
308                 }
309                 else if (s->char_at(*offset) == '-')
310                 {
311                     ++is;
312                     ++(*offset);
313                     negative = true;
314                 }
315             }
316 
317             // Parse integer part
318             double num = 0;
319             ssize_t in=0;
320             for ( ; *offset < len; ++(*offset), ++in)
321             {
322                 lsp_wchar_t wc = s->char_at(*offset);
323                 if ((wc >= '0') && (wc <= '9'))
324                     num = num * 10 + (wc - '0');
325                 else
326                     break;
327             }
328 
329             // No fraction part?
330             if (((*offset) >= len) || (s->char_at(*offset) != '.'))
331             {
332                 if (in <= 0)
333                     return STATUS_BAD_FORMAT;
334                 *dst = num;
335                 return STATUS_OK;
336             }
337             else
338                 ++(*offset);
339 
340             // Parse fraction part
341             double fnum = 0.1;
342             ssize_t fn=0;
343             for ( ; *offset < len; ++(*offset), ++fn)
344             {
345                 lsp_wchar_t wc = s->char_at(*offset);
346                 if ((wc >= '0') && (wc <= '9'))
347                 {
348                     num += (wc - '0') * fnum;
349                     fnum *= 0.1;
350                 }
351                 else
352                     break;
353             }
354 
355             // No integer and no fraction parts?
356             if ((in <= 0) && (fn <= 0))
357             {
358                 --(*offset);
359                 if (is > 0)
360                     --(*offset);
361                 return STATUS_BAD_FORMAT;
362             }
363 
364             // Return the result
365             *dst    = (negative) ? -num : num;
366             return STATUS_OK;
367         }
368 
parse_filter_settings(filter_t * f,const LSPString * s,size_t * offset)369         status_t parse_filter_settings(filter_t *f, const LSPString *s, size_t *offset)
370         {
371             status_t res;
372             if ((res = skip_whitespace(s, offset)) != STATUS_OK)
373                 return res;
374             LSPString tmp;
375 
376             /**
377              * Filter  6: ON  None
378              * Filter  7: ON  PK       Fc     100 Hz  Gain   0.0 dB  Q 10.000
379              * Filter  8: ON  Modal    Fc     100 Hz  Gain   0.0 dB  Q 13.643  T60 target   300 ms
380              */
381             // On/Off
382             if (s->starts_with_ascii_nocase("on ", *offset))
383             {
384                 *offset    += 3;
385                 f->enabled  = true;
386             }
387             else if (s->starts_with_ascii_nocase("off ", *offset))
388             {
389                 *offset    += 4;
390                 f->enabled  = false;
391             }
392             else
393                 return STATUS_BAD_FORMAT;
394 
395             if ((res = skip_whitespace(s, offset)) != STATUS_OK)
396                 return res;
397 
398             // Filter type
399             if (s->starts_with_ascii_nocase("none ", *offset))
400             {
401                 f->filterType   = NONE;
402                 *offset        += 5;
403             }
404             else if (s->starts_with_ascii_nocase("modal ", *offset))
405             {
406                 f->filterType   = MODAL;
407                 *offset        += 6;
408             }
409             else if (s->starts_with_ascii_nocase("pk ", *offset))
410             {
411                 f->filterType   = PK;
412                 *offset        += 3;
413             }
414             else if (s->starts_with_ascii_nocase("lp ", *offset))
415             {
416                 f->filterType   = LP;
417                 *offset        += 3;
418             }
419             else if (s->starts_with_ascii_nocase("hp ", *offset))
420             {
421                 f->filterType   = HP;
422                 *offset        += 3;
423             }
424             else if (s->starts_with_ascii_nocase("lpq ", *offset))
425             {
426                 f->filterType   = LPQ;
427                 *offset        += 4;
428             }
429             else if (s->starts_with_ascii_nocase("hpq ", *offset))
430             {
431                 f->filterType   = HPQ;
432                 *offset        += 4;
433             }
434             else if (s->starts_with_ascii_nocase("ls 6dB ", *offset))
435             {
436                 f->filterType   = LS6;
437                 *offset        += 7;
438             }
439             else if (s->starts_with_ascii_nocase("ls 12dB ", *offset))
440             {
441                 f->filterType   = LS12;
442                 *offset        += 8;
443             }
444             else if (s->starts_with_ascii_nocase("ls ", *offset))
445             {
446                 f->filterType   = LS;
447                 *offset        += 3;
448             }
449             else if (s->starts_with_ascii_nocase("hs 6dB ", *offset))
450             {
451                 f->filterType   = HS6;
452                 *offset        += 7;
453             }
454             else if (s->starts_with_ascii_nocase("hs 12dB ", *offset))
455             {
456                 f->filterType   = HS12;
457                 *offset        += 8;
458             }
459             else if (s->starts_with_ascii_nocase("hs ", *offset))
460             {
461                 f->filterType   = HS;
462                 *offset        += 3;
463             }
464             else if (s->starts_with_ascii_nocase("no ", *offset))
465             {
466                 f->filterType   = NO;
467                 *offset        += 3;
468             }
469             else if (s->starts_with_ascii_nocase("ap ", *offset))
470             {
471                 f->filterType   = AP;
472                 *offset        += 3;
473             }
474             else
475                 return STATUS_BAD_FORMAT;
476 
477             // Other parameters
478             f->Q        = 1.0;
479             f->fc       = 100.0;
480             f->gain     = 0.0;
481 
482             if ((f->filterType == LP) || (f->filterType == HP))
483                 f->Q        = M_SQRT1_2;
484 
485             if ((res = skip_whitespace(s, offset)) != STATUS_OK)
486                 return res;
487 
488             // Scan the rest of line for optional parameters
489             size_t len = s->length();
490             lsp_trace("offset = %d, len = %d", int(*offset), int(len));
491 
492             while (*offset < len)
493             {
494                 lsp_trace("analyzing: %s", s->get_utf8(*offset));
495                 if (s->starts_with_ascii_nocase("fc ", *offset))
496                 {
497                     *offset += 3;
498                     if ((res = parse_double(&f->fc, s, offset)) != STATUS_OK)
499                         return res;
500                     if (f->fc < 0)
501                         return STATUS_BAD_FORMAT;
502 
503                     if ((res = skip_whitespace(s, offset)) != STATUS_OK)
504                         return res;
505                     if (!s->starts_with_ascii_nocase("hz ", *offset))
506                         return STATUS_BAD_FORMAT;
507                     *offset += 3;
508                 }
509                 else if (s->starts_with_ascii_nocase("gain ", *offset))
510                 {
511                     *offset += 5;
512                     if ((res = parse_double(&f->gain, s, offset)) != STATUS_OK)
513                         return res;
514 
515                     if ((res = skip_whitespace(s, offset)) != STATUS_OK)
516                         return res;
517                     if (!s->starts_with_ascii_nocase("db ", *offset))
518                         return STATUS_BAD_FORMAT;
519                     *offset += 3;
520                 }
521                 else if (s->starts_with_ascii_nocase("q ", *offset))
522                 {
523                     *offset += 2;
524                     if ((res = parse_double(&f->Q, s, offset)) != STATUS_OK)
525                         return res;
526                 }
527                 else
528                 {
529                     if ((res = skip_data(s, offset)) != STATUS_OK)
530                         return res;
531                 }
532 
533                 if ((res = skip_whitespace(s, offset)) != STATUS_OK)
534                     return res;
535             }
536 
537             return STATUS_OK;
538         }
539 
parse_text_config(io::IInSequence * is,config_t ** dst)540         status_t parse_text_config(io::IInSequence *is, config_t **dst)
541         {
542             LSPString s;
543             status_t res;
544 
545             // Read header
546             if ((res = is->read_line(&s, true)) != STATUS_OK)
547                 return res;
548             if (!s.equals_ascii("Filter Settings file"))
549                 return STATUS_UNSUPPORTED_FORMAT;
550 
551             // Read lines
552             LSPString notes, eq;
553             ssize_t major=0, minor=0;
554             size_t offset = 0;
555             cstorage<filter_t> vfilters;
556 
557             while ((res = is->read_line(&s, true)) == STATUS_OK)
558             {
559                 lsp_trace("Parsing line: %s", s.get_utf8());
560                 if (s.starts_with_ascii("Room EQ V"))
561                 {
562                     offset = 9;
563                     if ((res = parse_decimal(&major, &s, &offset)) != STATUS_OK)
564                         return res;
565                     if ((offset >= s.length()) || (s.char_at(offset) != '.'))
566                         return STATUS_BAD_FORMAT;
567                     ++offset;
568                     if ((res = parse_decimal(&minor, &s, &offset)) != STATUS_OK)
569                         return res;
570                 }
571                 else if (s.starts_with_ascii("Notes:"))
572                 {
573                     if (!notes.set(&s, 6))
574                         return STATUS_NO_MEM;
575                 }
576                 else if ((s.starts_with_ascii("Equaliser:")) || (s.starts_with_ascii("Equalizer:")))
577                 {
578                     offset = 10;
579                     if ((res = skip_whitespace(&s, &offset)) != STATUS_OK)
580                         return res;
581                     if (!eq.set(&s, offset))
582                         return STATUS_NO_MEM;
583                 }
584                 else if (s.starts_with_ascii("Filter "))
585                 {
586                     offset = 7;
587                     if (!s.append(' ')) // For easier parsing, we add a whitespace at the end
588                         return STATUS_NO_MEM;
589 
590                     // Find filter definition
591                     size_t len = s.length();
592                     while (offset < len)
593                         if (s.char_at(offset++) == ':')
594                             break;
595 
596                     // Allocate filter
597                     filter_t *f = vfilters.add();
598                     if (f == NULL)
599                         return STATUS_NO_MEM;
600 
601                     // Parse filter settings
602                     if ((res = parse_filter_settings(f, &s, &offset)) != STATUS_OK)
603                         return res;
604 
605                     lsp_trace(
606                             "Filter %d: %s [%d] Fc %.0f Hz Gain %.1f dB Q %.7f",
607                             int(vfilters.size()), (f->enabled) ? "ON" : "OFF", f->filterType,
608                             double(f->fc), double(f->gain), double(f->Q)
609                         );
610                 }
611             }
612 
613             // Analyze current status
614             if (res == STATUS_EOF)
615                 res = STATUS_OK;
616             else if (res != STATUS_OK)
617                 return res;
618 
619             // Now we are ready to allocate the data
620             config_t *cfg   = build_config(&eq, &notes, major, minor, vfilters.size());
621             if (cfg == NULL)
622                 return STATUS_NO_MEM;
623 
624             // Copy filter data
625             ::memcpy(cfg->vFilters, vfilters.get_array(), sizeof(filter_t) * vfilters.size());
626 
627             // Store the result
628             if (dst != NULL)
629                 *dst    = cfg;
630             else
631                 ::free(cfg);
632 
633             return STATUS_OK;
634         }
635 
load_text_file(io::IInStream * is,config_t ** dst,const char * charset)636         status_t load_text_file(io::IInStream *is, config_t **dst, const char *charset)
637         {
638             io::InSequence cs;
639             status_t res = cs.wrap(is, WRAP_NONE, charset);
640             if (res != STATUS_OK)
641             {
642                 cs.close();
643                 return res;
644             }
645 
646             res = parse_text_config(&cs, dst);
647             if (res == STATUS_OK)
648                 return cs.close();
649 
650             cs.close();
651             return res;
652         }
653 
load_text(io::IInStream * is,config_t ** dst)654         status_t load_text(io::IInStream *is, config_t **dst)
655         {
656             // Read UTF-16 signature (if present)
657             uint16_t signature;
658             status_t res = is->read_block(&signature, sizeof(signature));
659             if (res != STATUS_OK)
660                 return (res == STATUS_EOF) ? STATUS_BAD_FORMAT : res;
661 
662             signature = BE_TO_CPU(signature);
663             if (signature == 0xfeff) // UTF-16BE
664             {
665                 if ((res = load_text_file(is, dst, "UTF-16BE")) == STATUS_OK)
666                     return res;
667             }
668             else if (signature == 0xfffe) // UTF-16LE
669             {
670                 if ((res = load_text_file(is, dst, "UTF-16LE")) == STATUS_OK)
671                     return res;
672             }
673 
674             // Try to load unicode character sets
675             for (const char **cset=charsets; *cset != NULL; ++cset)
676             {
677                 if ((res = is->seek(0)) != STATUS_OK)
678                     return res;
679                 if ((res = load_text_file(is, dst, *cset)) == STATUS_OK)
680                     return res;
681             }
682 
683             if ((res = is->seek(0)) != STATUS_OK)
684                 return res;
685             return load_text_file(is, dst, NULL);
686         }
687 
688 
load(const char * path,config_t ** dst)689         status_t load(const char *path, config_t **dst)
690         {
691             if (path == NULL)
692                 return STATUS_BAD_ARGUMENTS;
693 
694             LSPString tmp;
695             if (!tmp.set_utf8(path))
696                 return STATUS_NO_MEM;
697 
698             return load(&tmp, dst);
699         }
700 
load(const LSPString * path,config_t ** dst)701         status_t load(const LSPString *path, config_t **dst)
702         {
703             if (path == NULL)
704                 return STATUS_BAD_ARGUMENTS;
705 
706             io::InFileStream ifs;
707             status_t res = ifs.open(path);
708             if (res != STATUS_OK)
709             {
710                 ifs.close();
711                 return res;
712             }
713 
714             res = load(&ifs, dst);
715             if (res != STATUS_OK)
716             {
717                 ifs.close();
718                 return res;
719             }
720 
721             return ifs.close();
722         }
723 
load(const io::Path * path,config_t ** dst)724         status_t load(const io::Path *path, config_t **dst)
725         {
726             if (path == NULL)
727                 return STATUS_BAD_ARGUMENTS;
728             return load(path->as_string(), dst);
729         }
730 
load(FILE * fd,config_t ** dst)731         status_t load(FILE *fd, config_t **dst)
732         {
733             if (fd == NULL)
734                 return STATUS_BAD_ARGUMENTS;
735 
736             io::InFileStream ifs;
737             status_t res = ifs.wrap(fd, false);
738             if (res != STATUS_OK)
739             {
740                 ifs.close();
741                 return res;
742             }
743             res = load(&ifs, dst);
744             if (res != STATUS_OK)
745             {
746                 ifs.close();
747                 return res;
748             }
749 
750             return ifs.close();
751         }
752 
load(io::File * fd,config_t ** dst)753         status_t load(io::File *fd, config_t **dst)
754         {
755             if (fd == NULL)
756                 return STATUS_BAD_ARGUMENTS;
757 
758             io::InFileStream ifs;
759             status_t res = ifs.wrap(fd, false);
760             if (res != STATUS_OK)
761             {
762                 ifs.close();
763                 return res;
764             }
765             res = load(&ifs, dst);
766             if (res != STATUS_OK)
767             {
768                 ifs.close();
769                 return res;
770             }
771 
772             return ifs.close();
773         }
774 
load(io::IInStream * is,config_t ** dst)775         status_t load(io::IInStream *is, config_t **dst)
776         {
777             if (is == NULL)
778                 return STATUS_BAD_ARGUMENTS;
779 
780             // We need to sink file to memory to detect the file format (java or text)
781             io::OutMemoryStream os;
782             wssize_t bytes = is->sink(&os);
783             if (bytes < 0)
784             {
785                 os.close();
786                 return -bytes;
787             }
788 
789             // Load the file
790             status_t res = load(os.data(), os.size(), dst);
791             if (res != STATUS_OK)
792             {
793                 os.close();
794                 return res;
795             }
796 
797             return os.close();
798         }
799 
800 
load(const void * data,size_t size,config_t ** dst)801         status_t load(const void *data, size_t size, config_t **dst)
802         {
803             if ((data == NULL) || (dst == NULL))
804                 return STATUS_BAD_ARGUMENTS;
805 
806             io::InMemoryStream is;
807             is.wrap(data, size);
808 
809             // Try to load java format
810             status_t res = load_java(&is, dst);
811             if (res == STATUS_OK)
812                 return is.close();
813             else if (res != STATUS_BAD_FORMAT)
814             {
815                 is.close();
816                 return res;
817             }
818 
819             // Try to load plain text format
820             is.seek(0);
821             res = load_text(&is, dst);
822             if (res == STATUS_OK)
823                 return is.close();
824 
825             is.close();
826             return res;
827         }
828     }
829 }
830 
831 
832