2005-10-29 20:27:43 +04:00
|
|
|
/*
|
|
|
|
* Copyright 2005, Ingo Weinhold, bonefish@users.sf.net.
|
|
|
|
* Distributed under the terms of the MIT License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
#include <List.h>
|
|
|
|
#include <Resources.h>
|
|
|
|
#include <StorageDefs.h>
|
|
|
|
#include <TypeConstants.h>
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
static const char *kCommandName = "xres";
|
|
|
|
static const char *kDefaultResourceName = NULL;
|
|
|
|
static const char *kDefaultOutputFile = "xres.output.rsrc";
|
|
|
|
static const int kMaxSaneResourceSize = 100 * 1024 * 1024; // 100 MB
|
|
|
|
|
|
|
|
static int kArgc;
|
|
|
|
static const char *const *kArgv;
|
|
|
|
|
|
|
|
// usage
|
|
|
|
const char *kUsage =
|
|
|
|
"Usage: %s ( -h | --help )\n"
|
|
|
|
" %s -l <file> ...\n"
|
|
|
|
" %s <command> ...\n"
|
|
|
|
"\n"
|
|
|
|
"The first form prints this help text and exits.\n"
|
|
|
|
"\n"
|
|
|
|
"The second form lists the resources of all given files.\n"
|
|
|
|
"\n"
|
|
|
|
"The third form manipulates the resources of one or more files according to\n"
|
|
|
|
"the given commands.\n"
|
|
|
|
"\n"
|
|
|
|
"Valid commands are:\n"
|
|
|
|
" <input file>\n"
|
|
|
|
" - Add the resources read from file <input file> to the current\n"
|
|
|
|
" output file. The file can either be a resource file or an\n"
|
|
|
|
" executable file.\n"
|
|
|
|
" -a <type>:<id>[:<name>] ( <file> | -s <data> )\n"
|
|
|
|
" - Add a resource to the current output file. The added resource is\n"
|
|
|
|
" of type <type> and has the ID <id>. If given the resource will\n"
|
|
|
|
" have name <name>, otherwise it won't have a name. The resource\n"
|
|
|
|
" data will either be the string <data> provided on the command\n"
|
|
|
|
" line or the data read from file <file> (the whole contents).\n"
|
|
|
|
" -d <type>[:<id>]\n"
|
|
|
|
" - Excludes resources with type <type> and, if given, ID <id> from\n"
|
|
|
|
" being written to the output file. This applies to all resources\n"
|
|
|
|
" read from input files or directly specified via command \"-a\"\n"
|
|
|
|
" following this command until the next \"-d\" command.\n"
|
|
|
|
" -o <output file>\n"
|
|
|
|
" - Changes the output file to <output file>. All resources specified\n"
|
|
|
|
" by subsequent <input file> or \"-a\" commands will be written\n"
|
|
|
|
" to this file until the next output file is specified via the\n"
|
|
|
|
" \"-o\" command. Resources specified later overwrite earlier ones\n"
|
|
|
|
" with the same type and ID. If <output file> doesn't exist yet, \n"
|
|
|
|
" a resource file with the name will be created. If it exists and\n"
|
|
|
|
" is an executable file, the resources will be added to it (if the\n"
|
|
|
|
" file already has resources, they will be removed before). If it\n"
|
|
|
|
" is a resource file or a file of unknown type, it will be\n"
|
|
|
|
" overwritten with a resource file containing the specified\n"
|
|
|
|
" resources. The initial output file is \"xres.output.rsrc\".\n"
|
|
|
|
" Note that an output file will only be created or modified, if at\n"
|
|
|
|
" least one <input file> or \"-a\" command is given for it.\n"
|
|
|
|
" -x <type>[:<id>]\n"
|
|
|
|
" - Only resources with type <type> and, if given, ID <id> will be\n"
|
|
|
|
" written to the output file. This applies to all resources\n"
|
|
|
|
" read from input files or directly specified via command \"-a\"\n"
|
|
|
|
" following this command until the next \"-x\" command.\n"
|
|
|
|
" -- - All following arguments, even if starting with a \"-\" character,\n"
|
|
|
|
" are treated as input file names.\n"
|
|
|
|
"\n"
|
|
|
|
"Parameters:\n"
|
|
|
|
" <type> - A type constant consisting of exactly four characters.\n"
|
|
|
|
" <id> - A positive or negative integer.\n"
|
|
|
|
;
|
|
|
|
|
|
|
|
// resource_type
|
|
|
|
static const char *
|
|
|
|
resource_type(type_code type)
|
|
|
|
{
|
|
|
|
static char typeString[5];
|
|
|
|
|
|
|
|
typeString[0] = type >> 24;
|
|
|
|
typeString[1] = (type >> 16) & 0xff;
|
|
|
|
typeString[2] = (type >> 8) & 0xff;
|
|
|
|
typeString[3] = type & 0xff;
|
|
|
|
typeString[4] = '\0';
|
|
|
|
|
|
|
|
return typeString;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ResourceID
|
|
|
|
struct ResourceID {
|
|
|
|
type_code type;
|
|
|
|
int32 id;
|
|
|
|
bool wildcardID;
|
|
|
|
|
|
|
|
ResourceID(type_code type = B_ANY_TYPE, int32 id = 0,
|
|
|
|
bool wildcardID = true)
|
|
|
|
: type(type),
|
|
|
|
id(id),
|
|
|
|
wildcardID(wildcardID)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
ResourceID(const ResourceID &other)
|
|
|
|
{
|
|
|
|
*this = other;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Matches(const ResourceID &other) const
|
|
|
|
{
|
|
|
|
return ((type == other.type || type == B_ANY_TYPE)
|
|
|
|
&& (wildcardID || id == other.id));
|
|
|
|
}
|
|
|
|
|
|
|
|
ResourceID &operator=(const ResourceID &other)
|
|
|
|
{
|
|
|
|
type = other.type;
|
|
|
|
id = other.id;
|
|
|
|
wildcardID = other.wildcardID;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// ResourceDataSource
|
|
|
|
struct ResourceDataSource {
|
|
|
|
ResourceDataSource()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual ~ResourceDataSource()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void GetData(const void *&data, size_t &size) = 0;
|
|
|
|
|
|
|
|
virtual void Flush()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// MemoryResourceDataSource
|
|
|
|
struct MemoryResourceDataSource : ResourceDataSource {
|
|
|
|
MemoryResourceDataSource(const void *data, size_t size, bool clone)
|
|
|
|
{
|
|
|
|
_Init(data, size, clone);
|
|
|
|
}
|
|
|
|
|
|
|
|
MemoryResourceDataSource(const char *data, bool clone)
|
|
|
|
{
|
|
|
|
_Init(data, strlen(data) + 1, clone);
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual ~MemoryResourceDataSource()
|
|
|
|
{
|
|
|
|
if (fOwner)
|
|
|
|
delete[] fData;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void GetData(const void *&data, size_t &size)
|
|
|
|
{
|
|
|
|
data = fData;
|
|
|
|
size = fSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void _Init(const void *data, size_t size, bool clone)
|
|
|
|
{
|
|
|
|
if (clone) {
|
|
|
|
fData = new uint8[size];
|
|
|
|
memcpy(fData, data, size);
|
|
|
|
fSize = size;
|
|
|
|
fOwner = true;
|
|
|
|
} else {
|
|
|
|
fData = (uint8*)data;
|
|
|
|
fSize = size;
|
|
|
|
fOwner = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
uint8 *fData;
|
|
|
|
size_t fSize;
|
|
|
|
bool fOwner;
|
|
|
|
};
|
|
|
|
|
|
|
|
// FileResourceDataSource
|
|
|
|
struct FileResourceDataSource : ResourceDataSource {
|
|
|
|
FileResourceDataSource(const char *path)
|
|
|
|
: fPath(path),
|
|
|
|
fData(NULL),
|
|
|
|
fSize(0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual ~FileResourceDataSource()
|
|
|
|
{
|
|
|
|
Flush();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void GetData(const void *&_data, size_t &_size)
|
|
|
|
{
|
|
|
|
if (!fData) {
|
|
|
|
// open the file for reading
|
|
|
|
BFile file;
|
|
|
|
status_t error = file.SetTo(fPath.c_str(), B_READ_ONLY);
|
|
|
|
if (error != B_OK) {
|
|
|
|
fprintf(stderr, "Error: Failed to open file \"%s\": %s\n",
|
|
|
|
fPath.c_str(), strerror(error));
|
|
|
|
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// get size
|
|
|
|
off_t size;
|
|
|
|
error = file.GetSize(&size);
|
|
|
|
if (error != B_OK) {
|
|
|
|
fprintf(stderr, "Error: Failed to get size of file \"%s\": "
|
|
|
|
"%s\n", fPath.c_str(), strerror(error));
|
|
|
|
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// check size
|
|
|
|
if (size > kMaxSaneResourceSize) {
|
|
|
|
fprintf(stderr, "Error: Resource data file \"%s\" is too big\n",
|
|
|
|
fPath.c_str());
|
|
|
|
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// read the data
|
|
|
|
fData = new uint8[size];
|
|
|
|
fSize = size;
|
|
|
|
ssize_t bytesRead = file.ReadAt(0, fData, fSize);
|
|
|
|
if (bytesRead < 0) {
|
|
|
|
fprintf(stderr, "Error: Failed to read data size from file "
|
|
|
|
"\"%s\": %s\n", fPath.c_str(), strerror(bytesRead));
|
|
|
|
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_data = fData;
|
|
|
|
_size = fSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void Flush()
|
|
|
|
{
|
|
|
|
if (fData) {
|
|
|
|
delete[] fData;
|
|
|
|
fData = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
string fPath;
|
|
|
|
uint8 *fData;
|
|
|
|
size_t fSize;
|
|
|
|
};
|
|
|
|
|
|
|
|
// State
|
|
|
|
struct State {
|
|
|
|
State()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual ~State()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void SetOutput(const char *path)
|
|
|
|
{
|
|
|
|
(void)path;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void ProcessInput(const char *path)
|
|
|
|
{
|
|
|
|
(void)path;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void SetInclusionPattern(const ResourceID &pattern)
|
|
|
|
{
|
|
|
|
(void)pattern;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void SetExclusionPattern(const ResourceID &pattern)
|
|
|
|
{
|
|
|
|
(void)pattern;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void AddResource(const ResourceID &id, const char *name,
|
|
|
|
ResourceDataSource *dataSource)
|
|
|
|
{
|
|
|
|
(void)id;
|
|
|
|
(void)name;
|
|
|
|
(void)dataSource;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// ListState
|
|
|
|
struct ListState : State {
|
|
|
|
ListState()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual ~ListState()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void ProcessInput(const char *path)
|
|
|
|
{
|
|
|
|
// open the file for reading
|
|
|
|
BFile file;
|
|
|
|
status_t error = file.SetTo(path, B_READ_ONLY);
|
|
|
|
if (error != B_OK) {
|
|
|
|
fprintf(stderr, "Error: Failed to open file \"%s\": %s\n", path,
|
|
|
|
strerror(error));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// open the resources
|
|
|
|
BResources resources;
|
|
|
|
error = resources.SetTo(&file, false);
|
|
|
|
if (error != B_OK) {
|
|
|
|
if (error == B_ERROR) {
|
|
|
|
fprintf(stderr, "Error: File \"%s\" is not a resource file.\n",
|
|
|
|
path);
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Error: Failed to read resources from file "
|
|
|
|
"\"%s\": %s\n", path, strerror(error));
|
|
|
|
}
|
|
|
|
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// print resources
|
|
|
|
printf("\n%s resources:\n\n", path);
|
|
|
|
printf(" type ID size name\n");
|
|
|
|
printf("------ ----------- ----------- --------------------\n");
|
|
|
|
|
|
|
|
type_code type;
|
|
|
|
int32 id;
|
|
|
|
const char *name;
|
|
|
|
size_t size;
|
|
|
|
for (int32 i = 0;
|
|
|
|
resources.GetResourceInfo(i, &type, &id, &name, &size);
|
|
|
|
i++) {
|
|
|
|
|
2009-05-04 23:08:27 +04:00
|
|
|
printf("'%s' %11ld %11u %s\n", resource_type(type), id, size,
|
2005-10-29 20:27:43 +04:00
|
|
|
(name && strlen(name) > 0 ? name : "(no name)"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// WriteFileState
|
|
|
|
struct WriteFileState : State {
|
|
|
|
WriteFileState()
|
|
|
|
: fOutputFilePath(kDefaultOutputFile),
|
|
|
|
fResources(NULL),
|
|
|
|
fInclusionPattern(NULL),
|
|
|
|
fExclusionPattern(NULL)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual ~WriteFileState()
|
|
|
|
{
|
|
|
|
_FlushOutput();
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void SetOutput(const char *path)
|
|
|
|
{
|
|
|
|
_FlushOutput();
|
|
|
|
|
|
|
|
fOutputFilePath = path;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void ProcessInput(const char *path)
|
|
|
|
{
|
|
|
|
// open the file for reading
|
|
|
|
BFile file;
|
|
|
|
status_t error = file.SetTo(path, B_READ_ONLY);
|
|
|
|
if (error != B_OK) {
|
|
|
|
fprintf(stderr, "Error: Failed to open input file \"%s\": %s\n",
|
|
|
|
path, strerror(error));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// open the resources
|
|
|
|
BResources resources;
|
|
|
|
error = resources.SetTo(&file, false);
|
|
|
|
if (error != B_OK) {
|
|
|
|
if (error == B_ERROR) {
|
|
|
|
fprintf(stderr, "Error: Input file \"%s\" is not a resource "
|
|
|
|
"file.\n", path);
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Error: Failed to read resources from input "
|
|
|
|
"file \"%s\": %s\n", path, strerror(error));
|
|
|
|
}
|
|
|
|
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
resources.PreloadResourceType();
|
|
|
|
|
|
|
|
// add resources
|
|
|
|
type_code type;
|
|
|
|
int32 id;
|
|
|
|
const char *name;
|
|
|
|
size_t size;
|
|
|
|
for (int32 i = 0;
|
|
|
|
resources.GetResourceInfo(i, &type, &id, &name, &size);
|
|
|
|
i++) {
|
|
|
|
// load the resource
|
|
|
|
const void *data = resources.LoadResource(type, id, &size);
|
|
|
|
if (!data) {
|
|
|
|
fprintf(stderr, "Error: Failed to read resources from input "
|
|
|
|
"file \"%s\".\n", path);
|
|
|
|
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// add it
|
|
|
|
MemoryResourceDataSource dataSource(data, size, false);
|
|
|
|
AddResource(ResourceID(type, id), name, &dataSource);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void SetInclusionPattern(const ResourceID &pattern)
|
|
|
|
{
|
|
|
|
if (!fInclusionPattern)
|
|
|
|
fInclusionPattern = new ResourceID;
|
|
|
|
*fInclusionPattern = pattern;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void SetExclusionPattern(const ResourceID &pattern)
|
|
|
|
{
|
|
|
|
if (!fExclusionPattern)
|
|
|
|
fExclusionPattern = new ResourceID;
|
|
|
|
*fExclusionPattern = pattern;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void AddResource(const ResourceID &id, const char *name,
|
|
|
|
ResourceDataSource *dataSource)
|
|
|
|
{
|
|
|
|
_PrepareOutput();
|
|
|
|
|
|
|
|
// filter resource
|
2009-05-04 23:08:27 +04:00
|
|
|
if ((fInclusionPattern && !fInclusionPattern->Matches(id))
|
|
|
|
|| (fExclusionPattern && fExclusionPattern->Matches(id))) {
|
2005-10-29 20:27:43 +04:00
|
|
|
// not included or explicitly excluded
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// get resource data
|
|
|
|
const void *data;
|
|
|
|
size_t size;
|
|
|
|
dataSource->GetData(data, size);
|
|
|
|
|
|
|
|
// add the resource
|
|
|
|
status_t error = fResources->AddResource(id.type, id.id, data, size,
|
|
|
|
name);
|
|
|
|
if (error != B_OK) {
|
|
|
|
fprintf(stderr, "Error: Failed to add resource type '%s', ID %ld "
|
|
|
|
"to output file \"%s\": %s\n", resource_type(id.type), id.id,
|
|
|
|
fOutputFilePath.c_str(), strerror(error));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void _FlushOutput()
|
|
|
|
{
|
|
|
|
if (fResources) {
|
|
|
|
status_t error = fResources->Sync();
|
|
|
|
if (error != B_OK) {
|
|
|
|
fprintf(stderr, "Error: Failed to write resources to output "
|
|
|
|
"file \"%s\": %s\n", fOutputFilePath.c_str(),
|
|
|
|
strerror(error));
|
|
|
|
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
delete fResources;
|
|
|
|
fResources = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void _PrepareOutput()
|
|
|
|
{
|
|
|
|
if (fResources)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// open the file for writing
|
|
|
|
BFile file;
|
|
|
|
status_t error = file.SetTo(fOutputFilePath.c_str(),
|
|
|
|
B_READ_WRITE | B_CREATE_FILE);
|
|
|
|
if (error != B_OK) {
|
|
|
|
fprintf(stderr, "Error: Failed to open output file \"%s\": %s\n",
|
|
|
|
fOutputFilePath.c_str(), strerror(error));
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// open the resources
|
|
|
|
fResources = new BResources;
|
|
|
|
error = fResources->SetTo(&file, true);
|
|
|
|
if (error != B_OK) {
|
|
|
|
fprintf(stderr, "Error: Failed to init resources for output "
|
|
|
|
"file \"%s\": %s\n", fOutputFilePath.c_str(), strerror(error));
|
|
|
|
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
string fOutputFilePath;
|
|
|
|
BResources *fResources;
|
|
|
|
ResourceID *fInclusionPattern;
|
|
|
|
ResourceID *fExclusionPattern;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Command
|
|
|
|
struct Command {
|
|
|
|
Command()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual ~Command()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void Do(State *state) = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
// SetOutputCommand
|
|
|
|
struct SetOutputCommand : Command {
|
|
|
|
SetOutputCommand(const char *path)
|
|
|
|
: Command(),
|
|
|
|
fPath(path)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void Do(State *state)
|
|
|
|
{
|
|
|
|
state->SetOutput(fPath.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
string fPath;
|
|
|
|
};
|
|
|
|
|
|
|
|
// ProcessInputCommand
|
|
|
|
struct ProcessInputCommand : Command {
|
|
|
|
ProcessInputCommand(const char *path)
|
|
|
|
: Command(),
|
|
|
|
fPath(path)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void Do(State *state)
|
|
|
|
{
|
|
|
|
state->ProcessInput(fPath.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
string fPath;
|
|
|
|
};
|
|
|
|
|
|
|
|
// SetResourcePatternCommand
|
|
|
|
struct SetResourcePatternCommand : Command {
|
|
|
|
SetResourcePatternCommand(const ResourceID &pattern, bool inclusion)
|
|
|
|
: Command(),
|
|
|
|
fPattern(pattern),
|
|
|
|
fInclusion(inclusion)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void Do(State *state)
|
|
|
|
{
|
|
|
|
if (fInclusion)
|
|
|
|
state->SetInclusionPattern(fPattern);
|
|
|
|
else
|
|
|
|
state->SetExclusionPattern(fPattern);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
ResourceID fPattern;
|
|
|
|
bool fInclusion;
|
|
|
|
};
|
|
|
|
|
|
|
|
// AddResourceCommand
|
|
|
|
struct AddResourceCommand : Command {
|
|
|
|
AddResourceCommand(const ResourceID &id, const char *name,
|
|
|
|
ResourceDataSource *dataSource)
|
|
|
|
: Command(),
|
|
|
|
fID(id),
|
|
|
|
fHasName(name),
|
|
|
|
fDataSource(dataSource)
|
|
|
|
{
|
|
|
|
if (fHasName)
|
|
|
|
fName = name;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual ~AddResourceCommand()
|
|
|
|
{
|
|
|
|
delete fDataSource;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void Do(State *state)
|
|
|
|
{
|
|
|
|
state->AddResource(fID, (fHasName ? fName.c_str() : NULL),
|
|
|
|
fDataSource);
|
|
|
|
fDataSource->Flush();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
ResourceID fID;
|
|
|
|
string fName;
|
|
|
|
bool fHasName;
|
|
|
|
ResourceDataSource *fDataSource;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// print_usage
|
|
|
|
static void
|
|
|
|
print_usage(bool error)
|
|
|
|
{
|
|
|
|
// get command name
|
|
|
|
const char *commandName = NULL;
|
|
|
|
if (kArgc > 0) {
|
|
|
|
if (const char *lastSlash = strchr(kArgv[0], '/'))
|
|
|
|
commandName = lastSlash + 1;
|
|
|
|
else
|
|
|
|
commandName = kArgv[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!commandName || strlen(commandName) == 0)
|
|
|
|
commandName = kCommandName;
|
|
|
|
|
|
|
|
// print usage
|
|
|
|
fprintf((error ? stderr : stdout), kUsage, commandName, commandName,
|
|
|
|
commandName);
|
|
|
|
}
|
|
|
|
|
|
|
|
// print_usage_and_exit
|
|
|
|
static void
|
|
|
|
print_usage_and_exit(bool error)
|
|
|
|
{
|
|
|
|
print_usage(error);
|
|
|
|
exit(error ? 1 : 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// next_arg
|
|
|
|
static const char *
|
|
|
|
next_arg(int &argi, bool optional = false)
|
|
|
|
{
|
|
|
|
if (argi >= kArgc) {
|
|
|
|
if (!optional)
|
|
|
|
print_usage_and_exit(true);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return kArgv[argi++];
|
|
|
|
}
|
|
|
|
|
|
|
|
// parse_resource_id
|
|
|
|
static void
|
|
|
|
parse_resource_id(const char *toParse, ResourceID &resourceID,
|
|
|
|
const char **name = NULL)
|
|
|
|
{
|
|
|
|
int len = strlen(toParse);
|
|
|
|
|
|
|
|
// type
|
|
|
|
if (len < 4)
|
|
|
|
print_usage_and_exit(true);
|
|
|
|
|
|
|
|
resourceID.type = ((int32)toParse[0] << 24) | ((int32)toParse[1] << 16)
|
|
|
|
| ((int32)toParse[2] << 8) | (int32)toParse[3];
|
|
|
|
|
|
|
|
if (toParse[4] == '\0') {
|
|
|
|
// if a name can be provided, the ID is mandatory
|
|
|
|
if (name)
|
|
|
|
print_usage_and_exit(true);
|
|
|
|
|
|
|
|
resourceID.id = 0;
|
|
|
|
resourceID.wildcardID = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (toParse[4] != ':')
|
|
|
|
print_usage_and_exit(true);
|
|
|
|
|
|
|
|
toParse += 5;
|
|
|
|
len -= 5;
|
|
|
|
|
|
|
|
// ID
|
|
|
|
bool negative = false;
|
|
|
|
if (*toParse == '-') {
|
|
|
|
negative = true;
|
|
|
|
toParse++;
|
|
|
|
len--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*toParse < '0' || *toParse > '9')
|
|
|
|
print_usage_and_exit(true);
|
|
|
|
|
|
|
|
int id = 0;
|
|
|
|
while (*toParse >= '0' && *toParse <= '9') {
|
|
|
|
id = 10 * id + (*toParse - '0');
|
|
|
|
toParse++;
|
|
|
|
len--;
|
|
|
|
}
|
|
|
|
|
|
|
|
resourceID.wildcardID = false;
|
|
|
|
resourceID.id = (negative ? -id : id);
|
|
|
|
|
|
|
|
if (*toParse == '\0') {
|
|
|
|
if (name)
|
|
|
|
*name = kDefaultResourceName;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*toParse != ':')
|
|
|
|
print_usage_and_exit(true);
|
|
|
|
|
|
|
|
// the remainder is name
|
|
|
|
*name = toParse + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// main
|
|
|
|
int
|
|
|
|
main(int argc, const char *const *argv)
|
|
|
|
{
|
|
|
|
kArgc = argc;
|
|
|
|
kArgv = argv;
|
|
|
|
|
|
|
|
if (argc < 2)
|
|
|
|
print_usage_and_exit(true);
|
|
|
|
|
|
|
|
BList commandList;
|
|
|
|
|
|
|
|
// parse the arguments
|
|
|
|
bool noMoreOptions = false;
|
|
|
|
bool list = false;
|
|
|
|
bool noList = false;
|
|
|
|
bool hasInputFiles = false;
|
|
|
|
for (int argi = 1; argi < argc; ) {
|
|
|
|
const char *arg = argv[argi++];
|
|
|
|
if (!noMoreOptions && arg[0] == '-') {
|
|
|
|
if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0)
|
|
|
|
print_usage_and_exit(false);
|
|
|
|
|
|
|
|
if (strlen(arg) != 2)
|
|
|
|
print_usage_and_exit(true);
|
|
|
|
|
|
|
|
switch (arg[1]) {
|
|
|
|
case 'a':
|
|
|
|
{
|
|
|
|
noList = true;
|
|
|
|
|
|
|
|
// get id
|
|
|
|
const char *typeString = next_arg(argi);
|
|
|
|
ResourceID resourceID;
|
|
|
|
const char *name = NULL;
|
|
|
|
parse_resource_id(typeString, resourceID, &name);
|
|
|
|
|
|
|
|
// get data
|
|
|
|
const char *file = next_arg(argi);
|
|
|
|
ResourceDataSource *dataSource;
|
|
|
|
if (strcmp(file, "-s") == 0) {
|
|
|
|
const char *data = next_arg(argi);
|
|
|
|
dataSource = new MemoryResourceDataSource(data, false);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
dataSource = new FileResourceDataSource(file);
|
|
|
|
}
|
|
|
|
|
|
|
|
// add command
|
|
|
|
Command *command = new AddResourceCommand(resourceID,
|
|
|
|
name, dataSource);
|
|
|
|
commandList.AddItem(command);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'd':
|
|
|
|
{
|
|
|
|
noList = true;
|
|
|
|
|
|
|
|
// get pattern
|
|
|
|
const char *typeString = next_arg(argi);
|
|
|
|
ResourceID pattern;
|
|
|
|
parse_resource_id(typeString, pattern);
|
|
|
|
|
|
|
|
// add command
|
|
|
|
Command *command = new SetResourcePatternCommand(pattern,
|
|
|
|
false);
|
|
|
|
commandList.AddItem(command);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'l':
|
|
|
|
{
|
|
|
|
list = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'o':
|
|
|
|
{
|
|
|
|
noList = true;
|
|
|
|
|
|
|
|
// get file name
|
|
|
|
const char *out = next_arg(argi);
|
|
|
|
|
|
|
|
// add command
|
|
|
|
Command *command = new SetOutputCommand(out);
|
|
|
|
commandList.AddItem(command);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case 'x':
|
|
|
|
{
|
|
|
|
noList = true;
|
|
|
|
|
|
|
|
// get pattern
|
|
|
|
const char *typeString = next_arg(argi);
|
|
|
|
ResourceID pattern;
|
|
|
|
parse_resource_id(typeString, pattern);
|
|
|
|
|
|
|
|
// add command
|
|
|
|
Command *command = new SetResourcePatternCommand(pattern,
|
|
|
|
true);
|
|
|
|
commandList.AddItem(command);
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case '-':
|
|
|
|
noMoreOptions = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
print_usage_and_exit(true);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// input file
|
|
|
|
hasInputFiles = true;
|
|
|
|
Command *command = new ProcessInputCommand(arg);
|
|
|
|
commandList.AddItem(command);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// don't allow "-l" together with other comands or without at least one
|
|
|
|
// input file
|
2009-05-04 23:08:27 +04:00
|
|
|
if ((list && noList) || (list && !hasInputFiles))
|
2005-10-29 20:27:43 +04:00
|
|
|
print_usage_and_exit(true);
|
|
|
|
|
|
|
|
// create a state
|
|
|
|
State *state;
|
|
|
|
if (list)
|
|
|
|
state = new ListState();
|
|
|
|
else
|
|
|
|
state = new WriteFileState();
|
|
|
|
|
|
|
|
// process commands
|
|
|
|
for (int32 i = 0; Command *command = (Command*)commandList.ItemAt(i); i++)
|
|
|
|
command->Do(state);
|
|
|
|
|
|
|
|
// delete state (will flush resources)
|
|
|
|
delete state;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|