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
23using namespace BPrivate::Storage;
24using 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.
34BResources::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.
47BResources::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.
61BResources::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.
75BResources::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
88BResources::~BResources()
89{
90	Unset();
91	delete fContainer;
92}
93
94
95// Initialized the BResources object to represent the resources of
96// the supplied file.
97status_t
98BResources::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.
135status_t
136BResources::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.
158status_t
159BResources::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.
181status_t
182BResources::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.
203status_t
204BResources::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.
231void
232BResources::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.
248status_t
249BResources::InitCheck() const
250{
251	return (fContainer ? B_OK : B_NO_MEMORY);
252}
253
254
255// Gets a reference to the internal BFile object.
256const BFile&
257BResources::File() const
258{
259	return fFile;
260}
261
262
263// Loads a resource identified by type and id into memory.
264const void*
265BResources::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.
290const void*
291BResources::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.
316status_t
317BResources::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.
341status_t
342BResources::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.
363status_t
364BResources::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.
385status_t
386BResources::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.
415status_t
416BResources::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.
449bool
450BResources::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.
458bool
459BResources::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.
466bool
467BResources::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.
488bool
489BResources::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.
510bool
511BResources::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.
528bool
529BResources::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.
546bool
547BResources::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.
568status_t
569BResources::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.
589status_t
590BResources::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).
612status_t
613BResources::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).
648status_t
649BResources::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).
677void*
678BResources::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).
695void*
696BResources::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
712void BResources::_ReservedResources1() {}
713void BResources::_ReservedResources2() {}
714void BResources::_ReservedResources3() {}
715void BResources::_ReservedResources4() {}
716void BResources::_ReservedResources5() {}
717void BResources::_ReservedResources6() {}
718void BResources::_ReservedResources7() {}
719void BResources::_ReservedResources8() {}
720