1 /* 2 * Copyright 2001-2006, Ingo Weinhold <bonefish@cs.tu-berlin.de>. 3 * Copyright 2013 Haiku, Inc. 4 * All Rights Reserved. Distributed under the terms of the MIT License. 5 * 6 * Authors: 7 * John Scipione, jscipione@gmail.com 8 * Ingo Weinhold, bonefish@cs.tu-berlin.de 9 */ 10 11 12 #include <Resources.h> 13 14 #include <new> 15 #include <stdio.h> 16 #include <stdlib.h> 17 18 #include "ResourceFile.h" 19 #include "ResourceItem.h" 20 #include "ResourcesContainer.h" 21 22 23 using namespace BPrivate::Storage; 24 using namespace std; 25 26 27 // debugging 28 //#define DBG(x) x 29 #define DBG(x) 30 #define OUT printf 31 32 33 // Creates an unitialized BResources object. 34 BResources::BResources() 35 : 36 fFile(), 37 fContainer(NULL), 38 fResourceFile(NULL), 39 fReadOnly(false) 40 { 41 fContainer = new(nothrow) ResourcesContainer; 42 } 43 44 45 // Creates a BResources object that represents the resources of the 46 // supplied file. 47 BResources::BResources(const BFile* file, bool clobber) 48 : 49 fFile(), 50 fContainer(NULL), 51 fResourceFile(NULL), 52 fReadOnly(false) 53 { 54 fContainer = new(nothrow) ResourcesContainer; 55 SetTo(file, clobber); 56 } 57 58 59 // Creates a BResources object that represents the resources of the 60 // file referenced by the supplied path. 61 BResources::BResources(const char* path, bool clobber) 62 : 63 fFile(), 64 fContainer(NULL), 65 fResourceFile(NULL), 66 fReadOnly(false) 67 { 68 fContainer = new(nothrow) ResourcesContainer; 69 SetTo(path, clobber); 70 } 71 72 73 // Creates a BResources object that represents the resources of the 74 // file referenced by the supplied ref. 75 BResources::BResources(const entry_ref* ref, bool clobber) 76 : 77 fFile(), 78 fContainer(NULL), 79 fResourceFile(NULL), 80 fReadOnly(false) 81 { 82 fContainer = new(nothrow) ResourcesContainer; 83 SetTo(ref, clobber); 84 } 85 86 87 // Frees all resources associated with this object 88 BResources::~BResources() 89 { 90 Unset(); 91 delete fContainer; 92 } 93 94 95 // Initialized the BResources object to represent the resources of 96 // the supplied file. 97 status_t 98 BResources::SetTo(const BFile* file, bool clobber) 99 { 100 Unset(); 101 status_t error = B_OK; 102 if (file) { 103 error = file->InitCheck(); 104 if (error == B_OK) { 105 fFile = *file; 106 error = fFile.InitCheck(); 107 } 108 if (error == B_OK) { 109 fReadOnly = !fFile.IsWritable(); 110 fResourceFile = new(nothrow) ResourceFile; 111 if (fResourceFile) 112 error = fResourceFile->SetTo(&fFile, clobber); 113 else 114 error = B_NO_MEMORY; 115 } 116 if (error == B_OK) { 117 if (fContainer) 118 error = fResourceFile->InitContainer(*fContainer); 119 else 120 error = B_NO_MEMORY; 121 } 122 } 123 if (error != B_OK) { 124 delete fResourceFile; 125 fResourceFile = NULL; 126 if (fContainer) 127 fContainer->MakeEmpty(); 128 } 129 return error; 130 } 131 132 133 // Initialized the BResources object to represent the resources of 134 // the file referred to by the supplied path. 135 status_t 136 BResources::SetTo(const char* path, bool clobber) 137 { 138 if (!path) 139 return B_BAD_VALUE; 140 141 // open file 142 BFile file; 143 status_t error = file.SetTo(path, B_READ_WRITE); 144 if (error != B_OK) { 145 Unset(); 146 return error; 147 } 148 149 // delegate the actual work 150 return SetTo(&file, clobber); 151 } 152 153 154 // Initialized the BResources object to represent the resources of the 155 // file referenced by the supplied ref. 156 status_t 157 BResources::SetTo(const entry_ref* ref, bool clobber) 158 { 159 if (!ref) 160 return B_BAD_VALUE; 161 162 // open file 163 BFile file; 164 status_t error = file.SetTo(ref, B_READ_WRITE); 165 if (error != B_OK) { 166 Unset(); 167 return error; 168 } 169 170 // delegate the actual work 171 return SetTo(&file, clobber); 172 } 173 174 175 // Initialized the BResources object to represent the resources of 176 // the file from which the specified image has been loaded. 177 status_t 178 BResources::SetToImage(image_id image, bool clobber) 179 { 180 #ifdef HAIKU_TARGET_PLATFORM_HAIKU 181 // get an image info 182 image_info info; 183 status_t error = get_image_info(image, &info); 184 if (error != B_OK) { 185 Unset(); 186 return error; 187 } 188 189 // delegate the actual work 190 return SetTo(info.name, clobber); 191 #else // HAIKU_TARGET_PLATFORM_HAIKU 192 return B_NOT_SUPPORTED; 193 #endif 194 } 195 196 197 // Initialized the BResources object to represent the resources of 198 // the file from which the specified pointer has been loaded. 199 status_t 200 BResources::SetToImage(const void* codeOrDataPointer, bool clobber) 201 { 202 #ifdef HAIKU_TARGET_PLATFORM_HAIKU 203 if (!codeOrDataPointer) 204 return B_BAD_VALUE; 205 206 // iterate through the images and find the one in question 207 addr_t address = (addr_t)codeOrDataPointer; 208 image_info info; 209 int32 cookie = 0; 210 211 while (get_next_image_info(B_CURRENT_TEAM, &cookie, &info) == B_OK) { 212 if (((addr_t)info.text <= address 213 && address - (addr_t)info.text < (addr_t)info.text_size) 214 || ((addr_t)info.data <= address 215 && address - (addr_t)info.data < (addr_t)info.data_size)) { 216 return SetTo(info.name, clobber); 217 } 218 } 219 220 return B_ENTRY_NOT_FOUND; 221 #else // HAIKU_TARGET_PLATFORM_HAIKU 222 return B_NOT_SUPPORTED; 223 #endif 224 } 225 226 227 // Returns the BResources object to an uninitialized state. 228 void 229 BResources::Unset() 230 { 231 if (fContainer && fContainer->IsModified()) 232 Sync(); 233 delete fResourceFile; 234 fResourceFile = NULL; 235 fFile.Unset(); 236 if (fContainer) 237 fContainer->MakeEmpty(); 238 else 239 fContainer = new(nothrow) ResourcesContainer; 240 fReadOnly = false; 241 } 242 243 244 // Gets the initialization status of the object. 245 status_t 246 BResources::InitCheck() const 247 { 248 return (fContainer ? B_OK : B_NO_MEMORY); 249 } 250 251 252 // Gets a reference to the internal BFile object. 253 const BFile& 254 BResources::File() const 255 { 256 return fFile; 257 } 258 259 260 // Loads a resource identified by type and id into memory. 261 const void* 262 BResources::LoadResource(type_code type, int32 id, size_t* _size) 263 { 264 // find the resource 265 status_t error = InitCheck(); 266 ResourceItem* resource = NULL; 267 if (error == B_OK) { 268 resource = fContainer->ResourceAt(fContainer->IndexOf(type, id)); 269 if (!resource) 270 error = B_ENTRY_NOT_FOUND; 271 } 272 // load it, if necessary 273 if (error == B_OK && !resource->IsLoaded() && fResourceFile) 274 error = fResourceFile->ReadResource(*resource); 275 // return the result 276 const void *result = NULL; 277 if (error == B_OK) { 278 result = resource->Data(); 279 if (_size) 280 *_size = resource->DataSize(); 281 } 282 return result; 283 } 284 285 286 // Loads a resource identified by type and name into memory. 287 const void* 288 BResources::LoadResource(type_code type, const char* name, size_t* _size) 289 { 290 // find the resource 291 status_t error = InitCheck(); 292 ResourceItem* resource = NULL; 293 if (error == B_OK) { 294 resource = fContainer->ResourceAt(fContainer->IndexOf(type, name)); 295 if (!resource) 296 error = B_ENTRY_NOT_FOUND; 297 } 298 // load it, if necessary 299 if (error == B_OK && !resource->IsLoaded() && fResourceFile) 300 error = fResourceFile->ReadResource(*resource); 301 // return the result 302 const void* result = NULL; 303 if (error == B_OK) { 304 result = resource->Data(); 305 if (_size) 306 *_size = resource->DataSize(); 307 } 308 return result; 309 } 310 311 312 // Loads all resources of the specified type into memory. 313 status_t 314 BResources::PreloadResourceType(type_code type) 315 { 316 status_t error = InitCheck(); 317 if (error == B_OK && fResourceFile) { 318 if (type == 0) 319 error = fResourceFile->ReadResources(*fContainer); 320 else { 321 int32 count = fContainer->CountResources(); 322 int32 errorCount = 0; 323 for (int32 i = 0; i < count; i++) { 324 ResourceItem *resource = fContainer->ResourceAt(i); 325 if (resource->Type() == type) { 326 if (fResourceFile->ReadResource(*resource) != B_OK) 327 errorCount++; 328 } 329 } 330 error = -errorCount; 331 } 332 } 333 return error; 334 } 335 336 337 // Writes all changes to the resources to the file. 338 status_t 339 BResources::Sync() 340 { 341 status_t error = InitCheck(); 342 if (error == B_OK) 343 error = fFile.InitCheck(); 344 if (error == B_OK) { 345 if (fReadOnly) 346 error = B_NOT_ALLOWED; 347 else if (!fResourceFile) 348 error = B_FILE_ERROR; 349 } 350 if (error == B_OK) 351 error = fResourceFile->ReadResources(*fContainer); 352 if (error == B_OK) 353 error = fResourceFile->WriteResources(*fContainer); 354 return error; 355 } 356 357 358 // Adds the resources of fromFile to the internal file of the 359 // BResources object. 360 status_t 361 BResources::MergeFrom(BFile* fromFile) 362 { 363 status_t error = (fromFile ? B_OK : B_BAD_VALUE); 364 if (error == B_OK) 365 error = InitCheck(); 366 if (error == B_OK) { 367 ResourceFile resourceFile; 368 error = resourceFile.SetTo(fromFile); 369 ResourcesContainer container; 370 if (error == B_OK) 371 error = resourceFile.InitContainer(container); 372 if (error == B_OK) 373 error = resourceFile.ReadResources(container); 374 if (error == B_OK) 375 fContainer->AssimilateResources(container); 376 } 377 return error; 378 } 379 380 381 // Writes the resources to a new file. 382 status_t 383 BResources::WriteTo(BFile* file) 384 { 385 status_t error = (file ? B_OK : B_BAD_VALUE); 386 if (error == B_OK) 387 error = InitCheck(); 388 // make sure, that all resources are loaded 389 if (error == B_OK && fResourceFile) { 390 error = fResourceFile->ReadResources(*fContainer); 391 fResourceFile->Unset(); 392 } 393 // set the new file, but keep the old container 394 if (error == B_OK) { 395 ResourcesContainer *container = fContainer; 396 fContainer = new(nothrow) ResourcesContainer; 397 if (fContainer) { 398 error = SetTo(file, false); 399 delete fContainer; 400 } else 401 error = B_NO_MEMORY; 402 fContainer = container; 403 } 404 // write the resources 405 if (error == B_OK && fResourceFile) 406 error = fResourceFile->WriteResources(*fContainer); 407 return error; 408 } 409 410 411 // Adds a new resource to the file. 412 status_t 413 BResources::AddResource(type_code type, int32 id, const void* data, 414 size_t length, const char* name) 415 { 416 status_t error = (data ? B_OK : B_BAD_VALUE); 417 if (error == B_OK) 418 error = InitCheck(); 419 if (error == B_OK) 420 error = (fReadOnly ? B_NOT_ALLOWED : B_OK); 421 if (error == B_OK) { 422 ResourceItem* item = new(nothrow) ResourceItem; 423 if (!item) 424 error = B_NO_MEMORY; 425 if (error == B_OK) { 426 item->SetIdentity(type, id, name); 427 ssize_t written = item->WriteAt(0, data, length); 428 if (written < 0) 429 error = written; 430 else if (written != (ssize_t)length) 431 error = B_ERROR; 432 } 433 if (error == B_OK) { 434 if (!fContainer->AddResource(item)) 435 error = B_NO_MEMORY; 436 } 437 if (error != B_OK) 438 delete item; 439 } 440 return error; 441 } 442 443 444 // Returns whether the file contains a resource with the specified 445 // type and id. 446 bool 447 BResources::HasResource(type_code type, int32 id) 448 { 449 return (InitCheck() == B_OK && fContainer->IndexOf(type, id) >= 0); 450 } 451 452 453 // Returns whether the file contains a resource with the specified 454 // type and name. 455 bool 456 BResources::HasResource(type_code type, const char* name) 457 { 458 return (InitCheck() == B_OK && fContainer->IndexOf(type, name) >= 0); 459 } 460 461 462 // Gets information about a resource identified by byindex. 463 bool 464 BResources::GetResourceInfo(int32 byIndex, type_code* typeFound, 465 int32* idFound, const char** nameFound, size_t* lengthFound) 466 { 467 ResourceItem* item = NULL; 468 if (InitCheck() == B_OK) 469 item = fContainer->ResourceAt(byIndex); 470 if (item) { 471 if (typeFound) 472 *typeFound = item->Type(); 473 if (idFound) 474 *idFound = item->ID(); 475 if (nameFound) 476 *nameFound = item->Name(); 477 if (lengthFound) 478 *lengthFound = item->DataSize(); 479 } 480 return item; 481 } 482 483 484 // Gets information about a resource identified by byType and andIndex. 485 bool 486 BResources::GetResourceInfo(type_code byType, int32 andIndex, int32* idFound, 487 const char** nameFound, size_t* lengthFound) 488 { 489 ResourceItem* item = NULL; 490 if (InitCheck() == B_OK) { 491 item = fContainer->ResourceAt(fContainer->IndexOfType(byType, 492 andIndex)); 493 } 494 if (item) { 495 if (idFound) 496 *idFound = item->ID(); 497 if (nameFound) 498 *nameFound = item->Name(); 499 if (lengthFound) 500 *lengthFound = item->DataSize(); 501 } 502 return item; 503 } 504 505 506 // Gets information about a resource identified by byType and andID. 507 bool 508 BResources::GetResourceInfo(type_code byType, int32 andID, 509 const char** nameFound, size_t* lengthFound) 510 { 511 ResourceItem* item = NULL; 512 if (InitCheck() == B_OK) 513 item = fContainer->ResourceAt(fContainer->IndexOf(byType, andID)); 514 if (item) { 515 if (nameFound) 516 *nameFound = item->Name(); 517 if (lengthFound) 518 *lengthFound = item->DataSize(); 519 } 520 return item; 521 } 522 523 524 // Gets information about a resource identified by byType and andName. 525 bool 526 BResources::GetResourceInfo(type_code byType, const char* andName, 527 int32* idFound, size_t* lengthFound) 528 { 529 ResourceItem* item = NULL; 530 if (InitCheck() == B_OK) 531 item = fContainer->ResourceAt(fContainer->IndexOf(byType, andName)); 532 if (item) { 533 if (idFound) 534 *idFound = item->ID(); 535 if (lengthFound) 536 *lengthFound = item->DataSize(); 537 } 538 return item; 539 } 540 541 542 // Gets information about a resource identified by byPointer. 543 bool 544 BResources::GetResourceInfo(const void* byPointer, type_code* typeFound, 545 int32* idFound, size_t* lengthFound, const char** nameFound) 546 { 547 ResourceItem* item = NULL; 548 if (InitCheck() == B_OK) 549 item = fContainer->ResourceAt(fContainer->IndexOf(byPointer)); 550 if (item) { 551 if (typeFound) 552 *typeFound = item->Type(); 553 if (idFound) 554 *idFound = item->ID(); 555 if (nameFound) 556 *nameFound = item->Name(); 557 if (lengthFound) 558 *lengthFound = item->DataSize(); 559 } 560 return item; 561 } 562 563 564 // Removes a resource identified by its data pointer. 565 status_t 566 BResources::RemoveResource(const void* resource) 567 { 568 status_t error = (resource ? B_OK : B_BAD_VALUE); 569 if (error == B_OK) 570 error = InitCheck(); 571 if (error == B_OK) 572 error = (fReadOnly ? B_NOT_ALLOWED : B_OK); 573 if (error == B_OK) { 574 ResourceItem* item 575 = fContainer->RemoveResource(fContainer->IndexOf(resource)); 576 if (item) 577 delete item; 578 else 579 error = B_BAD_VALUE; 580 } 581 return error; 582 } 583 584 585 // Removes a resource identified by type and id. 586 status_t 587 BResources::RemoveResource(type_code type, int32 id) 588 { 589 status_t error = InitCheck(); 590 if (error == B_OK) 591 error = (fReadOnly ? B_NOT_ALLOWED : B_OK); 592 if (error == B_OK) { 593 ResourceItem* item 594 = fContainer->RemoveResource(fContainer->IndexOf(type, id)); 595 if (item) 596 delete item; 597 else 598 error = B_BAD_VALUE; 599 } 600 return error; 601 } 602 603 604 // #pragma mark - deprecated methods 605 606 607 // Writes data into an existing resource 608 // (deprecated, use AddResource() instead). 609 status_t 610 BResources::WriteResource(type_code type, int32 id, const void* data, 611 off_t offset, size_t length) 612 { 613 status_t error = (data && offset >= 0 ? B_OK : B_BAD_VALUE); 614 if (error == B_OK) 615 error = InitCheck(); 616 if (error == B_OK) 617 error = (fReadOnly ? B_NOT_ALLOWED : B_OK); 618 ResourceItem *item = NULL; 619 if (error == B_OK) { 620 item = fContainer->ResourceAt(fContainer->IndexOf(type, id)); 621 if (!item) 622 error = B_BAD_VALUE; 623 } 624 if (error == B_OK && fResourceFile) 625 error = fResourceFile->ReadResource(*item); 626 if (error == B_OK) { 627 if (item) { 628 ssize_t written = item->WriteAt(offset, data, length); 629 if (written < 0) 630 error = written; 631 else if (written != (ssize_t)length) 632 error = B_ERROR; 633 } 634 } 635 return error; 636 } 637 638 639 // Reads data from an existing resource 640 // (deprecated, use LoadResource() instead). 641 status_t 642 BResources::ReadResource(type_code type, int32 id, void* data, off_t offset, 643 size_t length) 644 { 645 status_t error = (data && offset >= 0 ? B_OK : B_BAD_VALUE); 646 if (error == B_OK) 647 error = InitCheck(); 648 ResourceItem* item = NULL; 649 if (error == B_OK) { 650 item = fContainer->ResourceAt(fContainer->IndexOf(type, id)); 651 if (!item) 652 error = B_BAD_VALUE; 653 } 654 if (error == B_OK && fResourceFile) 655 error = fResourceFile->ReadResource(*item); 656 if (error == B_OK) { 657 if (item) { 658 ssize_t read = item->ReadAt(offset, data, length); 659 if (read < 0) 660 error = read; 661 } else 662 error = B_BAD_VALUE; 663 } 664 return error; 665 } 666 667 668 // Finds a resource by type and id and returns a pointer to a copy of 669 // its data (deprecated, use LoadResource() instead). 670 void* 671 BResources::FindResource(type_code type, int32 id, size_t* lengthFound) 672 { 673 void* result = NULL; 674 size_t size = 0; 675 const void* data = LoadResource(type, id, &size); 676 if (data != NULL) { 677 if ((result = malloc(size))) 678 memcpy(result, data, size); 679 } 680 if (lengthFound) 681 *lengthFound = size; 682 return result; 683 } 684 685 686 // Finds a resource by type and name and returns a pointer to a copy of 687 // its data (deprecated, use LoadResource() instead). 688 void* 689 BResources::FindResource(type_code type, const char* name, size_t* lengthFound) 690 { 691 void* result = NULL; 692 size_t size = 0; 693 const void *data = LoadResource(type, name, &size); 694 if (data != NULL) { 695 if ((result = malloc(size))) 696 memcpy(result, data, size); 697 } 698 if (lengthFound) 699 *lengthFound = size; 700 return result; 701 } 702 703 704 // FBC 705 void BResources::_ReservedResources1() {} 706 void BResources::_ReservedResources2() {} 707 void BResources::_ReservedResources3() {} 708 void BResources::_ReservedResources4() {} 709 void BResources::_ReservedResources5() {} 710 void BResources::_ReservedResources6() {} 711 void BResources::_ReservedResources7() {} 712 void BResources::_ReservedResources8() {} 713