xref: /haiku/src/kits/storage/Resources.cpp (revision e5d65858f2361fe0552495b61620c84dcee6bc00)
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