2 * Copyright 2005-2010, Ingo Weinhold, bonefish@users.sf.net.
3 * Distributed under the terms of the MIT License.
14 #include <Resources.h>
15 #include <StorageDefs.h>
16 #include <TypeConstants.h>
22 static const char *kCommandName
= "xres";
23 static const char *kDefaultResourceName
= NULL
;
24 static const char *kDefaultOutputFile
= "xres.output.rsrc";
25 static const int kMaxSaneResourceSize
= 100 * 1024 * 1024; // 100 MB
28 static const char *const *kArgv
;
32 "Usage: %s ( -h | --help )\n"
36 "The first form prints this help text and exits.\n"
38 "The second form lists the resources of all given files.\n"
40 "The third form manipulates the resources of one or more files according to\n"
41 "the given commands.\n"
43 "Valid commands are:\n"
45 " - Add the resources read from file <input file> to the current\n"
46 " output file. The file can either be a resource file or an\n"
48 " -a <type>:<id>[:<name>] ( <file> | -s <data> )\n"
49 " - Add a resource to the current output file. The added resource is\n"
50 " of type <type> and has the ID <id>. If given the resource will\n"
51 " have name <name>, otherwise it won't have a name. The resource\n"
52 " data will either be the string <data> provided on the command\n"
53 " line or the data read from file <file> (the whole contents).\n"
55 " - Excludes resources with type <type> and, if given, ID <id> from\n"
56 " being written to the output file. This applies to all resources\n"
57 " read from input files or directly specified via command \"-a\"\n"
58 " following this command until the next \"-d\" command.\n"
60 " - Changes the output file to <output file>. All resources specified\n"
61 " by subsequent <input file> or \"-a\" commands will be written\n"
62 " to this file until the next output file is specified via the\n"
63 " \"-o\" command. Resources specified later overwrite earlier ones\n"
64 " with the same type and ID. If <output file> doesn't exist yet, \n"
65 " a resource file with the name will be created. If it exists and\n"
66 " is an executable file, the resources will be added to it (if the\n"
67 " file already has resources, they will be removed before). If it\n"
68 " is a resource file or a file of unknown type, it will be\n"
69 " overwritten with a resource file containing the specified\n"
70 " resources. The initial output file is \"xres.output.rsrc\".\n"
71 " Note that an output file will only be created or modified, if at\n"
72 " least one <input file> or \"-a\" command is given for it.\n"
74 " - Only resources with type <type> and, if given, ID <id> will be\n"
75 " written to the output file. This applies to all resources\n"
76 " read from input files or directly specified via command \"-a\"\n"
77 " following this command until the next \"-x\" command.\n"
78 " -- - All following arguments, even if starting with a \"-\" character,\n"
79 " are treated as input file names.\n"
82 " <type> - A type constant consisting of exactly four characters.\n"
83 " <id> - A positive or negative integer.\n"
89 resource_type(type_code type
)
91 static char typeString
[5];
93 typeString
[0] = type
>> 24;
94 typeString
[1] = (type
>> 16) & 0xff;
95 typeString
[2] = (type
>> 8) & 0xff;
96 typeString
[3] = type
& 0xff;
109 ResourceID(type_code type
= B_ANY_TYPE
, int32 id
= 0,
110 bool wildcardID
= true)
114 wildcardID(wildcardID
)
118 ResourceID(const ResourceID
&other
)
123 bool Matches(const ResourceID
&other
) const
125 return ((type
== other
.type
|| type
== B_ANY_TYPE
)
126 && (wildcardID
|| id
== other
.id
));
129 ResourceID
&operator=(const ResourceID
&other
)
133 wildcardID
= other
.wildcardID
;
139 // ResourceDataSource
140 struct ResourceDataSource
{
145 virtual ~ResourceDataSource()
149 virtual void GetData(const void *&data
, size_t &size
) = 0;
157 // MemoryResourceDataSource
158 struct MemoryResourceDataSource
: ResourceDataSource
{
159 MemoryResourceDataSource(const void *data
, size_t size
, bool clone
)
161 _Init(data
, size
, clone
);
164 MemoryResourceDataSource(const char *data
, bool clone
)
166 _Init(data
, strlen(data
) + 1, clone
);
169 virtual ~MemoryResourceDataSource()
175 virtual void GetData(const void *&data
, size_t &size
)
182 void _Init(const void *data
, size_t size
, bool clone
)
185 fData
= new uint8
[size
];
186 memcpy(fData
, data
, size
);
190 fData
= (uint8
*)data
;
203 // FileResourceDataSource
204 struct FileResourceDataSource
: ResourceDataSource
{
205 FileResourceDataSource(const char *path
)
213 virtual ~FileResourceDataSource()
218 virtual void GetData(const void *&_data
, size_t &_size
)
221 // open the file for reading
223 status_t error
= file
.SetTo(fPath
.c_str(), B_READ_ONLY
);
225 fprintf(stderr
, "Error: Failed to open file \"%s\": %s\n",
226 fPath
.c_str(), strerror(error
));
233 error
= file
.GetSize(&size
);
235 fprintf(stderr
, "Error: Failed to get size of file \"%s\": "
236 "%s\n", fPath
.c_str(), strerror(error
));
242 if (size
> kMaxSaneResourceSize
) {
243 fprintf(stderr
, "Error: Resource data file \"%s\" is too big\n",
250 fData
= new uint8
[size
];
252 ssize_t bytesRead
= file
.ReadAt(0, fData
, fSize
);
254 fprintf(stderr
, "Error: Failed to read data size from file "
255 "\"%s\": %s\n", fPath
.c_str(), strerror(bytesRead
));
290 virtual void SetOutput(const char *path
)
295 virtual void ProcessInput(const char *path
)
300 virtual void SetInclusionPattern(const ResourceID
&pattern
)
305 virtual void SetExclusionPattern(const ResourceID
&pattern
)
310 virtual void AddResource(const ResourceID
&id
, const char *name
,
311 ResourceDataSource
*dataSource
)
321 struct ListState
: State
{
330 virtual void ProcessInput(const char *path
)
332 // open the file for reading
334 status_t error
= file
.SetTo(path
, B_READ_ONLY
);
336 fprintf(stderr
, "Error: Failed to open file \"%s\": %s\n", path
,
341 // open the resources
342 BResources resources
;
343 error
= resources
.SetTo(&file
, false);
345 if (error
== B_ERROR
) {
346 fprintf(stderr
, "Error: File \"%s\" is not a resource file.\n",
349 fprintf(stderr
, "Error: Failed to read resources from file "
350 "\"%s\": %s\n", path
, strerror(error
));
357 printf("\n%s resources:\n\n", path
);
358 printf(" type ID size name\n");
359 printf("------ ----------- ----------- --------------------\n");
366 resources
.GetResourceInfo(i
, &type
, &id
, &name
, &size
); i
++) {
367 printf("'%s' %11" B_PRId32
" %11" B_PRIuSIZE
" %s\n",
368 resource_type(type
), id
, size
,
369 name
!= NULL
&& name
[0] != '\0' ? name
: "(no name)");
376 struct WriteFileState
: State
{
379 fOutputFilePath(kDefaultOutputFile
),
381 fInclusionPattern(NULL
),
382 fExclusionPattern(NULL
)
386 virtual ~WriteFileState()
391 virtual void SetOutput(const char *path
)
395 fOutputFilePath
= path
;
398 virtual void ProcessInput(const char *path
)
400 // open the file for reading
402 status_t error
= file
.SetTo(path
, B_READ_ONLY
);
404 fprintf(stderr
, "Error: Failed to open input file \"%s\": %s\n",
405 path
, strerror(error
));
409 // open the resources
410 BResources resources
;
411 error
= resources
.SetTo(&file
, false);
413 if (error
== B_ERROR
) {
414 fprintf(stderr
, "Error: Input file \"%s\" is not a resource "
417 fprintf(stderr
, "Error: Failed to read resources from input "
418 "file \"%s\": %s\n", path
, strerror(error
));
423 resources
.PreloadResourceType();
431 resources
.GetResourceInfo(i
, &type
, &id
, &name
, &size
);
434 const void *data
= resources
.LoadResource(type
, id
, &size
);
436 fprintf(stderr
, "Error: Failed to read resources from input "
437 "file \"%s\".\n", path
);
443 MemoryResourceDataSource
dataSource(data
, size
, false);
444 AddResource(ResourceID(type
, id
), name
, &dataSource
);
448 virtual void SetInclusionPattern(const ResourceID
&pattern
)
450 if (!fInclusionPattern
)
451 fInclusionPattern
= new ResourceID
;
452 *fInclusionPattern
= pattern
;
455 virtual void SetExclusionPattern(const ResourceID
&pattern
)
457 if (!fExclusionPattern
)
458 fExclusionPattern
= new ResourceID
;
459 *fExclusionPattern
= pattern
;
462 virtual void AddResource(const ResourceID
&id
, const char *name
,
463 ResourceDataSource
*dataSource
)
468 if ((fInclusionPattern
&& !fInclusionPattern
->Matches(id
))
469 || (fExclusionPattern
&& fExclusionPattern
->Matches(id
))) {
470 // not included or explicitly excluded
477 dataSource
->GetData(data
, size
);
480 status_t error
= fResources
->AddResource(id
.type
, id
.id
, data
, size
,
483 fprintf(stderr
, "Error: Failed to add resource type '%s', ID %"
484 B_PRId32
" to output file \"%s\": %s\n", resource_type(id
.type
),
485 id
.id
, fOutputFilePath
.c_str(), strerror(error
));
494 status_t error
= fResources
->Sync();
496 fprintf(stderr
, "Error: Failed to write resources to output "
497 "file \"%s\": %s\n", fOutputFilePath
.c_str(),
508 void _PrepareOutput()
513 // open the file for writing
515 status_t error
= file
.SetTo(fOutputFilePath
.c_str(),
516 B_READ_WRITE
| B_CREATE_FILE
);
518 fprintf(stderr
, "Error: Failed to open output file \"%s\": %s\n",
519 fOutputFilePath
.c_str(), strerror(error
));
523 // open the resources
524 fResources
= new BResources
;
525 error
= fResources
->SetTo(&file
, true);
527 fprintf(stderr
, "Error: Failed to init resources for output "
528 "file \"%s\": %s\n", fOutputFilePath
.c_str(), strerror(error
));
535 string fOutputFilePath
;
536 BResources
*fResources
;
537 ResourceID
*fInclusionPattern
;
538 ResourceID
*fExclusionPattern
;
552 virtual void Do(State
*state
) = 0;
557 struct SetOutputCommand
: Command
{
558 SetOutputCommand(const char *path
)
565 virtual void Do(State
*state
)
567 state
->SetOutput(fPath
.c_str());
575 // ProcessInputCommand
576 struct ProcessInputCommand
: Command
{
577 ProcessInputCommand(const char *path
)
584 virtual void Do(State
*state
)
586 state
->ProcessInput(fPath
.c_str());
594 // SetResourcePatternCommand
595 struct SetResourcePatternCommand
: Command
{
596 SetResourcePatternCommand(const ResourceID
&pattern
, bool inclusion
)
600 fInclusion(inclusion
)
604 virtual void Do(State
*state
)
607 state
->SetInclusionPattern(fPattern
);
609 state
->SetExclusionPattern(fPattern
);
618 // AddResourceCommand
619 struct AddResourceCommand
: Command
{
620 AddResourceCommand(const ResourceID
&id
, const char *name
,
621 ResourceDataSource
*dataSource
)
626 fDataSource(dataSource
)
632 virtual ~AddResourceCommand()
637 virtual void Do(State
*state
)
639 state
->AddResource(fID
, (fHasName
? fName
.c_str() : NULL
),
641 fDataSource
->Flush();
648 ResourceDataSource
*fDataSource
;
654 print_usage(bool error
)
657 const char *commandName
= NULL
;
659 if (const char *lastSlash
= strchr(kArgv
[0], '/'))
660 commandName
= lastSlash
+ 1;
662 commandName
= kArgv
[0];
665 if (!commandName
|| commandName
[0] == '\0')
666 commandName
= kCommandName
;
669 fprintf((error
? stderr
: stdout
), kUsage
, commandName
, commandName
,
674 // print_usage_and_exit
676 print_usage_and_exit(bool error
)
685 next_arg(int &argi
, bool optional
= false)
689 print_usage_and_exit(true);
693 return kArgv
[argi
++];
699 parse_resource_id(const char *toParse
, ResourceID
&resourceID
,
700 const char **name
= NULL
)
702 int len
= strlen(toParse
);
706 print_usage_and_exit(true);
708 resourceID
.type
= ((int32
)toParse
[0] << 24) | ((int32
)toParse
[1] << 16)
709 | ((int32
)toParse
[2] << 8) | (int32
)toParse
[3];
711 if (toParse
[4] == '\0') {
712 // if a name can be provided, the ID is mandatory
714 print_usage_and_exit(true);
717 resourceID
.wildcardID
= true;
721 if (toParse
[4] != ':')
722 print_usage_and_exit(true);
728 bool negative
= false;
729 if (*toParse
== '-') {
735 if (*toParse
< '0' || *toParse
> '9')
736 print_usage_and_exit(true);
739 while (*toParse
>= '0' && *toParse
<= '9') {
740 id
= 10 * id
+ (*toParse
- '0');
745 resourceID
.wildcardID
= false;
746 resourceID
.id
= (negative
? -id
: id
);
748 if (*toParse
== '\0') {
750 *name
= kDefaultResourceName
;
756 print_usage_and_exit(true);
758 // the remainder is name
765 main(int argc
, const char *const *argv
)
771 print_usage_and_exit(true);
775 // parse the arguments
776 bool noMoreOptions
= false;
779 bool hasInputFiles
= false;
780 for (int argi
= 1; argi
< argc
; ) {
781 const char *arg
= argv
[argi
++];
782 if (!noMoreOptions
&& arg
[0] == '-') {
783 if (strcmp(arg
, "-h") == 0 || strcmp(arg
, "--help") == 0)
784 print_usage_and_exit(false);
786 if (strlen(arg
) != 2)
787 print_usage_and_exit(true);
795 const char *typeString
= next_arg(argi
);
796 ResourceID resourceID
;
797 const char *name
= NULL
;
798 parse_resource_id(typeString
, resourceID
, &name
);
801 const char *file
= next_arg(argi
);
802 ResourceDataSource
*dataSource
;
803 if (strcmp(file
, "-s") == 0) {
804 const char *data
= next_arg(argi
);
805 dataSource
= new MemoryResourceDataSource(data
, false);
808 dataSource
= new FileResourceDataSource(file
);
812 Command
*command
= new AddResourceCommand(resourceID
,
814 commandList
.AddItem(command
);
824 const char *typeString
= next_arg(argi
);
826 parse_resource_id(typeString
, pattern
);
829 Command
*command
= new SetResourcePatternCommand(pattern
,
831 commandList
.AddItem(command
);
847 const char *out
= next_arg(argi
);
850 Command
*command
= new SetOutputCommand(out
);
851 commandList
.AddItem(command
);
861 const char *typeString
= next_arg(argi
);
863 parse_resource_id(typeString
, pattern
);
866 Command
*command
= new SetResourcePatternCommand(pattern
,
868 commandList
.AddItem(command
);
874 noMoreOptions
= true;
878 print_usage_and_exit(true);
884 hasInputFiles
= true;
885 Command
*command
= new ProcessInputCommand(arg
);
886 commandList
.AddItem(command
);
890 // don't allow "-l" together with other comands or without at least one
892 if ((list
&& noList
) || (list
&& !hasInputFiles
))
893 print_usage_and_exit(true);
898 state
= new ListState();
900 state
= new WriteFileState();
903 for (int32 i
= 0; Command
*command
= (Command
*)commandList
.ItemAt(i
); i
++)
906 // delete state (will flush resources)