xref: /haiku/src/kits/storage/Resources.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
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 && error != B_ENTRY_NOT_FOUND)
145 		error = file.SetTo(path, B_READ_ONLY);
146 	if (error != B_OK) {
147 		Unset();
148 		return error;
149 	}
150 
151 	// delegate the actual work
152 	return SetTo(&file, clobber);
153 }
154 
155 
156 // Initialized the BResources object to represent the resources of the
157 // file referenced by the supplied ref.
158 status_t
159 BResources::SetTo(const entry_ref* ref, bool clobber)
160 {
161 	if (!ref)
162 		return B_BAD_VALUE;
163 
164 	// open file
165 	BFile file;
166 	status_t error = file.SetTo(ref, B_READ_WRITE);
167 	if (error != B_OK && error != B_ENTRY_NOT_FOUND)
168 		error = file.SetTo(ref, B_READ_ONLY);
169 	if (error != B_OK) {
170 		Unset();
171 		return error;
172 	}
173 
174 	// delegate the actual work
175 	return SetTo(&file, clobber);
176 }
177 
178 
179 // Initialized the BResources object to represent the resources of
180 // the file from which the specified image has been loaded.
181 status_t
182 BResources::SetToImage(image_id image, bool clobber)
183 {
184 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
185 	// get an image info
186 	image_info info;
187 	status_t error = get_image_info(image, &info);
188 	if (error != B_OK) {
189 		Unset();
190 		return error;
191 	}
192 
193 	// delegate the actual work
194 	return SetTo(info.name, clobber);
195 #else	// HAIKU_TARGET_PLATFORM_HAIKU
196 	return B_NOT_SUPPORTED;
197 #endif
198 }
199 
200 
201 // Initialized the BResources object to represent the resources of
202 // the file from which the specified pointer has been loaded.
203 status_t
204 BResources::SetToImage(const void* codeOrDataPointer, bool clobber)
205 {
206 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
207 	// iterate through the images and find the one in question
208 	addr_t address = (addr_t)codeOrDataPointer;
209 	image_info info;
210 	int32 cookie = 0;
211 
212 	while (get_next_image_info(B_CURRENT_TEAM, &cookie, &info) == B_OK) {
213 		if (address == 0
214 			? info.type == B_APP_IMAGE
215 			: (((addr_t)info.text <= address
216 					&& address - (addr_t)info.text < (addr_t)info.text_size)
217 				|| ((addr_t)info.data <= address
218 					&& address - (addr_t)info.data < (addr_t)info.data_size))) {
219 			return SetTo(info.name, clobber);
220 		}
221 	}
222 
223 	return B_ENTRY_NOT_FOUND;
224 #else	// HAIKU_TARGET_PLATFORM_HAIKU
225 	return B_NOT_SUPPORTED;
226 #endif
227 }
228 
229 
230 // Returns the BResources object to an uninitialized state.
231 void
232 BResources::Unset()
233 {
234 	if (fContainer && fContainer->IsModified())
235 		Sync();
236 	delete fResourceFile;
237 	fResourceFile = NULL;
238 	fFile.Unset();
239 	if (fContainer)
240 		fContainer->MakeEmpty();
241 	else
242 		fContainer = new(nothrow) ResourcesContainer;
243 	fReadOnly = false;
244 }
245 
246 
247 // Gets the initialization status of the object.
248 status_t
249 BResources::InitCheck() const
250 {
251 	return (fContainer ? B_OK : B_NO_MEMORY);
252 }
253 
254 
255 // Gets a reference to the internal BFile object.
256 const BFile&
257 BResources::File() const
258 {
259 	return fFile;
260 }
261 
262 
263 // Loads a resource identified by type and id into memory.
264 const void*
265 BResources::LoadResource(type_code type, int32 id, size_t* _size)
266 {
267 	// find the resource
268 	status_t error = InitCheck();
269 	ResourceItem* resource = NULL;
270 	if (error == B_OK) {
271 		resource = fContainer->ResourceAt(fContainer->IndexOf(type, id));
272 		if (!resource)
273 			error = B_ENTRY_NOT_FOUND;
274 	}
275 	// load it, if necessary
276 	if (error == B_OK && !resource->IsLoaded() && fResourceFile)
277 		error = fResourceFile->ReadResource(*resource);
278 	// return the result
279 	const void *result = NULL;
280 	if (error == B_OK) {
281 		result = resource->Data();
282 		if (_size)
283 			*_size = resource->DataSize();
284 	}
285 	return result;
286 }
287 
288 
289 // Loads a resource identified by type and name into memory.
290 const void*
291 BResources::LoadResource(type_code type, const char* name, size_t* _size)
292 {
293 	// find the resource
294 	status_t error = InitCheck();
295 	ResourceItem* resource = NULL;
296 	if (error == B_OK) {
297 		resource = fContainer->ResourceAt(fContainer->IndexOf(type, name));
298 		if (!resource)
299 			error = B_ENTRY_NOT_FOUND;
300 	}
301 	// load it, if necessary
302 	if (error == B_OK && !resource->IsLoaded() && fResourceFile)
303 		error = fResourceFile->ReadResource(*resource);
304 	// return the result
305 	const void* result = NULL;
306 	if (error == B_OK) {
307 		result = resource->Data();
308 		if (_size)
309 			*_size = resource->DataSize();
310 	}
311 	return result;
312 }
313 
314 
315 // Loads all resources of the specified type into memory.
316 status_t
317 BResources::PreloadResourceType(type_code type)
318 {
319 	status_t error = InitCheck();
320 	if (error == B_OK && fResourceFile) {
321 		if (type == 0)
322 			error = fResourceFile->ReadResources(*fContainer);
323 		else {
324 			int32 count = fContainer->CountResources();
325 			int32 errorCount = 0;
326 			for (int32 i = 0; i < count; i++) {
327 				ResourceItem *resource = fContainer->ResourceAt(i);
328 				if (resource->Type() == type) {
329 					if (fResourceFile->ReadResource(*resource) != B_OK)
330 						errorCount++;
331 				}
332 			}
333 			error = -errorCount;
334 		}
335 	}
336 	return error;
337 }
338 
339 
340 // Writes all changes to the resources to the file.
341 status_t
342 BResources::Sync()
343 {
344 	status_t error = InitCheck();
345 	if (error == B_OK)
346 		error = fFile.InitCheck();
347 	if (error == B_OK) {
348 		if (fReadOnly)
349 			error = B_NOT_ALLOWED;
350 		else if (!fResourceFile)
351 			error = B_FILE_ERROR;
352 	}
353 	if (error == B_OK)
354 		error = fResourceFile->ReadResources(*fContainer);
355 	if (error == B_OK)
356 		error = fResourceFile->WriteResources(*fContainer);
357 	return error;
358 }
359 
360 
361 // Adds the resources of fromFile to the internal file of the
362 // BResources object.
363 status_t
364 BResources::MergeFrom(BFile* fromFile)
365 {
366 	status_t error = (fromFile ? B_OK : B_BAD_VALUE);
367 	if (error == B_OK)
368 		error = InitCheck();
369 	if (error == B_OK) {
370 		ResourceFile resourceFile;
371 		error = resourceFile.SetTo(fromFile);
372 		ResourcesContainer container;
373 		if (error == B_OK)
374 			error = resourceFile.InitContainer(container);
375 		if (error == B_OK)
376 			error = resourceFile.ReadResources(container);
377 		if (error == B_OK)
378 			fContainer->AssimilateResources(container);
379 	}
380 	return error;
381 }
382 
383 
384 // Writes the resources to a new file.
385 status_t
386 BResources::WriteTo(BFile* file)
387 {
388 	status_t error = (file ? B_OK : B_BAD_VALUE);
389 	if (error == B_OK)
390 		error = InitCheck();
391 	// make sure, that all resources are loaded
392 	if (error == B_OK && fResourceFile) {
393 		error = fResourceFile->ReadResources(*fContainer);
394 		fResourceFile->Unset();
395 	}
396 	// set the new file, but keep the old container
397 	if (error == B_OK) {
398 		ResourcesContainer *container = fContainer;
399 		fContainer = new(nothrow) ResourcesContainer;
400 		if (fContainer) {
401 			error = SetTo(file, false);
402 			delete fContainer;
403 		} else
404 			error = B_NO_MEMORY;
405 		fContainer = container;
406 	}
407 	// write the resources
408 	if (error == B_OK && fResourceFile)
409 		error = fResourceFile->WriteResources(*fContainer);
410 	return error;
411 }
412 
413 
414 // Adds a new resource to the file.
415 status_t
416 BResources::AddResource(type_code type, int32 id, const void* data,
417 						size_t length, const char* name)
418 {
419 	status_t error = (data ? B_OK : B_BAD_VALUE);
420 	if (error == B_OK)
421 		error = InitCheck();
422 	if (error == B_OK)
423 		error = (fReadOnly ? B_NOT_ALLOWED : B_OK);
424 	if (error == B_OK) {
425 		ResourceItem* item = new(nothrow) ResourceItem;
426 		if (!item)
427 			error = B_NO_MEMORY;
428 		if (error == B_OK) {
429 			item->SetIdentity(type, id, name);
430 			ssize_t written = item->WriteAt(0, data, length);
431 			if (written < 0)
432 				error = written;
433 			else if (written != (ssize_t)length)
434 				error = B_ERROR;
435 		}
436 		if (error == B_OK) {
437 			if (!fContainer->AddResource(item))
438 				error = B_NO_MEMORY;
439 		}
440 		if (error != B_OK)
441 			delete item;
442 	}
443 	return error;
444 }
445 
446 
447 // Returns whether the file contains a resource with the specified
448 // type and id.
449 bool
450 BResources::HasResource(type_code type, int32 id)
451 {
452 	return (InitCheck() == B_OK && fContainer->IndexOf(type, id) >= 0);
453 }
454 
455 
456 // Returns whether the file contains a resource with the specified
457 // type and name.
458 bool
459 BResources::HasResource(type_code type, const char* name)
460 {
461 	return (InitCheck() == B_OK && fContainer->IndexOf(type, name) >= 0);
462 }
463 
464 
465 // Gets information about a resource identified by byindex.
466 bool
467 BResources::GetResourceInfo(int32 byIndex, type_code* typeFound,
468 	int32* idFound, const char** nameFound, size_t* lengthFound)
469 {
470 	ResourceItem* item = NULL;
471 	if (InitCheck() == B_OK)
472 		item = fContainer->ResourceAt(byIndex);
473 	if (item) {
474 		if (typeFound)
475 			*typeFound = item->Type();
476 		if (idFound)
477 			*idFound = item->ID();
478 		if (nameFound)
479 			*nameFound = item->Name();
480 		if (lengthFound)
481 			*lengthFound = item->DataSize();
482 	}
483 	return item;
484 }
485 
486 
487 // Gets information about a resource identified by byType and andIndex.
488 bool
489 BResources::GetResourceInfo(type_code byType, int32 andIndex, int32* idFound,
490 	const char** nameFound, size_t* lengthFound)
491 {
492 	ResourceItem* item = NULL;
493 	if (InitCheck() == B_OK) {
494 		item = fContainer->ResourceAt(fContainer->IndexOfType(byType,
495 															  andIndex));
496 	}
497 	if (item) {
498 		if (idFound)
499 			*idFound = item->ID();
500 		if (nameFound)
501 			*nameFound = item->Name();
502 		if (lengthFound)
503 			*lengthFound = item->DataSize();
504 	}
505 	return item;
506 }
507 
508 
509 // Gets information about a resource identified by byType and andID.
510 bool
511 BResources::GetResourceInfo(type_code byType, int32 andID,
512 	const char** nameFound, size_t* lengthFound)
513 {
514 	ResourceItem* item = NULL;
515 	if (InitCheck() == B_OK)
516 		item = fContainer->ResourceAt(fContainer->IndexOf(byType, andID));
517 	if (item) {
518 		if (nameFound)
519 			*nameFound = item->Name();
520 		if (lengthFound)
521 			*lengthFound = item->DataSize();
522 	}
523 	return item;
524 }
525 
526 
527 // Gets information about a resource identified by byType and andName.
528 bool
529 BResources::GetResourceInfo(type_code byType, const char* andName,
530 	int32* idFound, size_t* lengthFound)
531 {
532 	ResourceItem* item = NULL;
533 	if (InitCheck() == B_OK)
534 		item = fContainer->ResourceAt(fContainer->IndexOf(byType, andName));
535 	if (item) {
536 		if (idFound)
537 			*idFound = item->ID();
538 		if (lengthFound)
539 			*lengthFound = item->DataSize();
540 	}
541 	return item;
542 }
543 
544 
545 // Gets information about a resource identified by byPointer.
546 bool
547 BResources::GetResourceInfo(const void* byPointer, type_code* typeFound,
548 	int32* idFound, size_t* lengthFound, const char** nameFound)
549 {
550 	ResourceItem* item = NULL;
551 	if (InitCheck() == B_OK)
552 		item = fContainer->ResourceAt(fContainer->IndexOf(byPointer));
553 	if (item) {
554 		if (typeFound)
555 			*typeFound = item->Type();
556 		if (idFound)
557 			*idFound = item->ID();
558 		if (nameFound)
559 			*nameFound = item->Name();
560 		if (lengthFound)
561 			*lengthFound = item->DataSize();
562 	}
563 	return item;
564 }
565 
566 
567 // Removes a resource identified by its data pointer.
568 status_t
569 BResources::RemoveResource(const void* resource)
570 {
571 	status_t error = (resource ? B_OK : B_BAD_VALUE);
572 	if (error == B_OK)
573 		error = InitCheck();
574 	if (error == B_OK)
575 		error = (fReadOnly ? B_NOT_ALLOWED : B_OK);
576 	if (error == B_OK) {
577 		ResourceItem* item
578 			= fContainer->RemoveResource(fContainer->IndexOf(resource));
579 		if (item)
580 			delete item;
581 		else
582 			error = B_BAD_VALUE;
583 	}
584 	return error;
585 }
586 
587 
588 // Removes a resource identified by type and id.
589 status_t
590 BResources::RemoveResource(type_code type, int32 id)
591 {
592 	status_t error = InitCheck();
593 	if (error == B_OK)
594 		error = (fReadOnly ? B_NOT_ALLOWED : B_OK);
595 	if (error == B_OK) {
596 		ResourceItem* item
597 			= fContainer->RemoveResource(fContainer->IndexOf(type, id));
598 		if (item)
599 			delete item;
600 		else
601 			error = B_BAD_VALUE;
602 	}
603 	return error;
604 }
605 
606 
607 // #pragma mark - deprecated methods
608 
609 
610 // Writes data into an existing resource
611 // (deprecated, use AddResource() instead).
612 status_t
613 BResources::WriteResource(type_code type, int32 id, const void* data,
614 	off_t offset, size_t length)
615 {
616 	status_t error = (data && offset >= 0 ? B_OK : B_BAD_VALUE);
617 	if (error == B_OK)
618 		error = InitCheck();
619 	if (error == B_OK)
620 		error = (fReadOnly ? B_NOT_ALLOWED : B_OK);
621 
622 	if (error != B_OK)
623 		return error;
624 
625 	ResourceItem *item = fContainer->ResourceAt(fContainer->IndexOf(type, id));
626 	if (!item)
627 		return B_BAD_VALUE;
628 
629 	if (fResourceFile) {
630 		error = fResourceFile->ReadResource(*item);
631 		if (error != B_OK)
632 			return error;
633 	}
634 
635 	ssize_t written = item->WriteAt(offset, data, length);
636 
637 	if (written < 0)
638 		error = written;
639 	else if (written != (ssize_t)length)
640 		error = B_ERROR;
641 
642 	return error;
643 }
644 
645 
646 // Reads data from an existing resource
647 // (deprecated, use LoadResource() instead).
648 status_t
649 BResources::ReadResource(type_code type, int32 id, void* data, off_t offset,
650 	size_t length)
651 {
652 	status_t error = (data && offset >= 0 ? B_OK : B_BAD_VALUE);
653 	if (error == B_OK)
654 		error = InitCheck();
655 	ResourceItem* item = NULL;
656 	if (error == B_OK) {
657 		item = fContainer->ResourceAt(fContainer->IndexOf(type, id));
658 		if (!item)
659 			error = B_BAD_VALUE;
660 	}
661 	if (error == B_OK && fResourceFile)
662 		error = fResourceFile->ReadResource(*item);
663 	if (error == B_OK) {
664 		if (item) {
665 			ssize_t read = item->ReadAt(offset, data, length);
666 			if (read < 0)
667 				error = read;
668 		} else
669 			error = B_BAD_VALUE;
670 	}
671 	return error;
672 }
673 
674 
675 // Finds a resource by type and id and returns a pointer to a copy of
676 // its data (deprecated, use LoadResource() instead).
677 void*
678 BResources::FindResource(type_code type, int32 id, size_t* lengthFound)
679 {
680 	void* result = NULL;
681 	size_t size = 0;
682 	const void* data = LoadResource(type, id, &size);
683 	if (data != NULL) {
684 		if ((result = malloc(size)))
685 			memcpy(result, data, size);
686 	}
687 	if (lengthFound)
688 		*lengthFound = size;
689 	return result;
690 }
691 
692 
693 // Finds a resource by type and name and returns a pointer to a copy of
694 // its data (deprecated, use LoadResource() instead).
695 void*
696 BResources::FindResource(type_code type, const char* name, size_t* lengthFound)
697 {
698 	void* result = NULL;
699 	size_t size = 0;
700 	const void *data = LoadResource(type, name, &size);
701 	if (data != NULL) {
702 		if ((result = malloc(size)))
703 			memcpy(result, data, size);
704 	}
705 	if (lengthFound)
706 		*lengthFound = size;
707 	return result;
708 }
709 
710 
711 // FBC
712 void BResources::_ReservedResources1() {}
713 void BResources::_ReservedResources2() {}
714 void BResources::_ReservedResources3() {}
715 void BResources::_ReservedResources4() {}
716 void BResources::_ReservedResources5() {}
717 void BResources::_ReservedResources6() {}
718 void BResources::_ReservedResources7() {}
719 void BResources::_ReservedResources8() {}
720