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(¬es)) != 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, ¬es, 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, ¬es, 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