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