diff --git a/tools/pkg/unpkg.c b/tools/pkg/unpkg.c index c32d17e58..588cf6185 100644 --- a/tools/pkg/unpkg.c +++ b/tools/pkg/unpkg.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -37,12 +38,18 @@ struct options { char list; char print; char verbose; + enum { + Type_sc1, // PC version of SC1 + Type_pc, // PC version of SC2 + Type_3do, // 3DO version of SC2 + } type; }; -index_header *readIndex(const uint8 *buf); -void printIndex(const index_header *h, const uint8 *buf, FILE *out); -char *get_file_name(const uint8 *buf, const index_header *h, +index_header *readIndex(const struct options *opts, const uint8 *buf); +void printIndex(const struct options *opts, const index_header *h, + const uint8 *buf, FILE *out); +char *getFileName(const uint8 *buf, const index_header *h, uint16 file_index); void writeFiles(const index_header *h, const uint8 *data, const char *path); void parse_arguments(int argc, char *argv[], struct options *opts); @@ -78,9 +85,9 @@ main(int argc, char *argv[]) { } close(in); - h = readIndex(buf); + h = readIndex(&opts, buf); if (opts.print) - printIndex(h, buf, stdout); + printIndex(&opts, h, buf, stdout); if (opts.list) listResources(h, buf, stdout); @@ -89,8 +96,8 @@ main(int argc, char *argv[]) { size_t len; struct stat sb; - if (!h->res_flags) { - fprintf(stderr, "Error: Cannot unpack data from unpackaged " + if (!h->packaged) { + fprintf(stderr, "Error: Cannot unpack data from non-packaged " "file.\n"); exit(EXIT_FAILURE); } @@ -125,7 +132,12 @@ main(int argc, char *argv[]) { void usage() { - fprintf(stderr, "unpkg [-o outputdir] [-v] [-l listfile] \n"); + fprintf(stderr, "unpkg [-o ] [-l] [-p] [-v] \n" + "\t-o unpack to 'outputdir'\n" + "\t-l list the resources\n" + "\t-p print the index\n" + "\t-t package file type; one of \"sc1\", \"pc\", or \"3do\"\n" + "\t-v verbose mode\n"); } void @@ -133,8 +145,9 @@ parse_arguments(int argc, char *argv[], struct options *opts) { char ch; memset(opts, '\0', sizeof (struct options)); + opts->type = Type_3do; while (1) { - ch = getopt(argc, argv, "hlo:pv"); + ch = getopt(argc, argv, "hlo:pt:v"); if (ch == -1) break; switch(ch) { @@ -147,6 +160,25 @@ parse_arguments(int argc, char *argv[], struct options *opts) { case 'p': opts->print = 1; break; + case 't': + if (strcmp(optarg, "sc1") == 0 || + strcmp(optarg, "SC1") == 0) { + opts->type = Type_sc1; + } else if (strcmp(optarg, "pc") == 0 || + strcmp(optarg, "PC") == 0) { + opts->type = Type_pc; + } else if (strcmp(optarg, "3do") == 0 || + strcmp(optarg, "3DO") == 0) { + opts->type = Type_3do; + fprintf(stderr, "3DO package files are not yet " + "supported.\n"); + exit(EXIT_FAILURE); + } else { + fprintf(stderr, "Invalid package file type.\n"); + usage(); + exit(EXIT_FAILURE); + } + break; case 'v': opts->verbose = 1; break; @@ -167,21 +199,42 @@ parse_arguments(int argc, char *argv[], struct options *opts) { } index_header * -readIndex(const uint8 *buf) { +readIndex(const struct options *opts, const uint8 *buf) { index_header *h; + uint16 res_flags; const uint8 *bufptr; h = malloc(sizeof (index_header)); - h->res_flags = MAKE_WORD(buf[0x00], buf[0x01]) ? 1 : 0; - h->packmem_list_offs = - MAKE_DWORD(buf[0x02], buf[0x03], buf[0x04], buf[0x05]); - h->path_list_offs = - MAKE_DWORD(buf[0x06], buf[0x07], buf[0x08], buf[0x09]); - h->file_list_offs = - MAKE_DWORD(buf[0x0a], buf[0x0b], buf[0x0c], buf[0x0d]); - h->num_packages = MAKE_WORD(buf[0x0e], buf[0x0f]); - h->num_types = MAKE_WORD(buf[0x10], buf[0x11]); - h->header_len = MAKE_DWORD(buf[0x12], buf[0x13], buf[0x14], buf[0x15]); + res_flags = MAKE_WORD(buf[0x00], buf[0x01]); + h->packaged = ((res_flags & 0x1) == 0x1) ? true : false; + switch (opts->type) { + case Type_sc1: + h->path_list_offs = + MAKE_DWORD(buf[0x02], buf[0x03], buf[0x04], buf[0x05]); + h->file_list_offs = + MAKE_DWORD(buf[0x06], buf[0x07], buf[0x08], buf[0x09]); + h->packmem_list_offs = + MAKE_DWORD(buf[0x0a], buf[0x0b], buf[0x0c], buf[0x0d]); + h->num_types = MAKE_WORD(buf[0x0e], buf[0x0f]); + h->num_packages = MAKE_WORD(buf[0x12], buf[0x13]); + h->header_len = 0; + break; + case Type_pc: + abort(); // Not supported + break; + case Type_3do: + h->packmem_list_offs = + MAKE_DWORD(buf[0x02], buf[0x03], buf[0x04], buf[0x05]); + h->path_list_offs = + MAKE_DWORD(buf[0x06], buf[0x07], buf[0x08], buf[0x09]); + h->file_list_offs = + MAKE_DWORD(buf[0x0a], buf[0x0b], buf[0x0c], buf[0x0d]); + h->num_packages = MAKE_WORD(buf[0x0e], buf[0x0f]); + h->num_types = MAKE_WORD(buf[0x10], buf[0x11]); + h->header_len = MAKE_DWORD( + buf[0x12], buf[0x13], buf[0x14], buf[0x15]); + break; + } bufptr = buf + 0x16; h->package_list = malloc(h->num_packages * sizeof (package_desc)); @@ -221,65 +274,72 @@ readIndex(const uint8 *buf) { { int i, j; int num_types, num_instances; - uint32 temp; for (i = 0; i < h->num_packages; i++) { + package_desc *package = &h->package_list[i]; + uint32 next_offset; - if (h->res_flags) { - // packaged + if (h->packaged) { + // One file name per package char *temp; - temp = get_file_name(buf, h, h->package_list[i].file_index); - h->package_list[i].filename = - (temp == NULL) ? NULL : strdup(temp); + temp = getFileName(buf, h, package->file_index); + package->filename = (temp == NULL) ? NULL : strdup(temp); } else { - // not packaged - h->package_list[i].filename = NULL; + // File name is specified per resource instance. + package->filename = NULL; } - num_types = h->package_list[i].num_types; - h->package_list[i].type_list = - malloc(num_types * sizeof (packtype_desc)); + num_types = package->num_types; + package->type_list = malloc(num_types * sizeof (packtype_desc)); for (j = 0; j < num_types; j++) { + packtype_desc *type = &package->type_list[j]; + uint32 temp; + temp = MAKE_DWORD(bufptr[0], bufptr[1], bufptr[2], bufptr[3]); - h->package_list[i].type_list[j].type = GET_TYPE(temp); - h->package_list[i].type_list[j].first_instance = - GET_INSTANCE(temp); - h->package_list[i].type_list[j].num_instances = - GET_PACKAGE(temp); + type->type = GET_TYPE(temp); + type->first_instance = GET_INSTANCE(temp); + type->num_instances = GET_PACKAGE(temp); bufptr += 4; } - if (h->res_flags) { - // packaged - next_offset = h->package_list[i].data_loc; - } + next_offset = package->data_loc; for (j = 0; j < num_types; j++) { + packtype_desc *type = &package->type_list[j]; int k; - num_instances = h->package_list[i].type_list[j].num_instances; - h->package_list[i].type_list[j].instances = + num_instances = type->num_instances; + type->instances = malloc(sizeof (packdef_instance) * num_instances); for (k = 0; k < num_instances; k++) { - if (h->res_flags) { - // packaged - h->package_list[i].type_list[j].instances[k].size = - 4 * MAKE_WORD(bufptr[0], bufptr[1]); - h->package_list[i].type_list[j].instances[k].offset = - next_offset; - h->package_list[i].type_list[j].instances[k]. - filename = NULL; - next_offset += h->package_list[i].type_list[j]. - instances[k].size; + packdef_instance *instance = &type->instances[k]; + + if (h->packaged) { + switch (opts->type) { + case Type_sc1: + instance->size = + MAKE_WORD(bufptr[0], bufptr[1]); + break; + case Type_pc: + abort(); // Not yet supported. + break; + case Type_3do: + instance->size = + 4 * MAKE_WORD(bufptr[0], bufptr[1]); + break; + } + instance->offset = next_offset; + next_offset += instance->size; + instance->filename = NULL; + // File name is specified per package. } else { char *temp; - h->package_list[i].type_list[j].instances[k].size = 0; - h->package_list[i].type_list[j].instances[k].offset = - 0; - temp = get_file_name(buf, h, + instance->size = 0; + instance->offset = 0; + temp = getFileName(buf, h, MAKE_WORD(bufptr[0], bufptr[1])); - h->package_list[i].type_list[j].instances[k]. - filename = (temp == NULL) ? + instance->filename = (temp == NULL) ? NULL : strdup(temp); + } bufptr += 2; } // for k @@ -290,23 +350,47 @@ readIndex(const uint8 *buf) { } void -printIndex(const index_header *h, const uint8 *buf, FILE *out) { +printIndex(const struct options *opts, const index_header *h, + const uint8 *buf, FILE *out) { const uint8 *bufptr; fprintf(out, "0x00000000 File type: %s\n", - h->res_flags ? "packaged" : "not packaged"); - fprintf(out, "0x00000002 packmem_list_offs: 0x%08x\n", - h->packmem_list_offs); - fprintf(out, "0x00000006 path_list_offs: 0x%08x\n", - h->path_list_offs); - fprintf(out, "0x0000000a file_list_offs: 0x%08x\n", - h->file_list_offs); - fprintf(out, "0x0000000e Number of packages: %d\n", - h->num_packages); - fprintf(out, "0x00000010 Number of package types: %d\n", - h->num_types); - fprintf(out, "0x00000012 Header length: 0x%08x\n", - h->header_len); + h->packaged ? "packaged" : "not packaged"); + switch (opts->type) { + case Type_sc1: + fprintf(out, "0x00000002 path_list_offs: 0x%08x\n", + h->path_list_offs); + fprintf(out, "0x00000006 file_list_offs: 0x%08x\n", + h->file_list_offs); + fprintf(out, "0x0000000a packmem_list_offs: 0x%08x\n", + h->packmem_list_offs); + fprintf(out, "0x0000000e Number of package types: %d\n", + h->num_types); + fprintf(out, "0x00000010 Unknown: 0x%04x\n", + MAKE_WORD(buf[0x10], buf[0x11])); + fprintf(out, "0x00000012 Number of packages: %d\n", + h->num_packages); + fprintf(out, "0x00000013 Unknown: 0x%04x\n", + MAKE_WORD(buf[0x13], buf[0x14])); + break; + case Type_pc: + abort(); // Not supported + break; + case Type_3do: + fprintf(out, "0x00000002 packmem_list_offs: 0x%08x\n", + h->packmem_list_offs); + fprintf(out, "0x00000006 path_list_offs: 0x%08x\n", + h->path_list_offs); + fprintf(out, "0x0000000a file_list_offs: 0x%08x\n", + h->file_list_offs); + fprintf(out, "0x0000000e Number of packages: %d\n", + h->num_packages); + fprintf(out, "0x00000010 Number of package types: %d\n", + h->num_types); + fprintf(out, "0x00000012 Header length: 0x%08x\n", + h->header_len); + break; + } bufptr = buf + 0x16; fprintf(out, "0x00000016 package info:\n"); @@ -353,19 +437,18 @@ printIndex(const index_header *h, const uint8 *buf, FILE *out) { int num_types, num_instances; for (i = 0; i < h->num_packages; i++) { - if (h->res_flags) { - // packaged + package_desc *package = &h->package_list[i]; + + if (h->packaged) { fprintf(out, "0x%08x Package #%d (%s):\n", - bufptr - buf, i + 1, - (h->package_list[i].filename == NULL) ? - "<>" : h->package_list[i].filename); + bufptr - buf, i + 1, (package->filename == NULL) ? + "<>" : package->filename); } else { - // not packaged fprintf(out, "0x%08x Package #%d:\n", bufptr - buf, i + 1); } - num_types = h->package_list[i].num_types; + num_types = package->num_types; for (j = 0; j < num_types; j++) { fprintf(out, "0x%08x Type #%d: type %d, " "%d instances starting with #%d\n", @@ -377,30 +460,28 @@ printIndex(const index_header *h, const uint8 *buf, FILE *out) { } for (j = 0; j < num_types; j++) { + packtype_desc *type = &package->type_list[j]; int k; - num_instances = h->package_list[i].type_list[j].num_instances; + num_instances = type->num_instances; for (k = 0; k < num_instances; k++) { - if (h->res_flags) { - // packaged + packdef_instance *instance = &type->instances[k]; + + if (h->packaged) { fprintf(out, "0x%08x Instance #%d of " "type %d: size %d bytes\n", bufptr - buf, - k + h->package_list[i].type_list[j]. - first_instance, - h->package_list[i].type_list[j].type, - h->package_list[i].type_list[j].instances[k]. - size); + k + type->first_instance, type->type, + instance->size); } else { fprintf(out, "0x%08x Instance #%d of " "type %d: %s\n", bufptr - buf, - k + h->package_list[i].type_list[j]. - first_instance, - h->package_list[i].type_list[j].type, - (h->package_list[i].type_list[j].instances[k]. - filename == NULL) ? "<>" : - h->package_list[i].type_list[j].instances[k]. - filename); + k + type->first_instance, type->type, + (instance->filename == NULL) ? + "<>" : instance->filename); + // NB. NULL filenames should not occur for + // non-packaged files. } + bufptr += 2; } // for k } // for j @@ -415,29 +496,27 @@ listResources(const index_header *h, const uint8 *buf, FILE *out) { char *filename; for (i = 0; i < h->num_packages; i++) { - if (h->res_flags) { - // packaged - filename = (h->package_list[i].filename == NULL) ? - "<>" : h->package_list[i].filename; + package_desc *package = &h->package_list[i]; + + if (h->packaged) { + // One filename for all resources in a package. + filename = (package->filename == NULL) ? + "<>" : package->filename; } - num_types = h->package_list[i].num_types; + num_types = package->num_types; for (j = 0; j < num_types; j++) { + packtype_desc *type = &package->type_list[j]; int k; - num_instances = h->package_list[i].type_list[j].num_instances; + num_instances = type->num_instances; for (k = 0; k < num_instances; k++) { - if (!h->res_flags) { - filename = (h->package_list[i].type_list[j].instances[k]. - filename == NULL) ? "<>" : - h->package_list[i].type_list[j].instances[k]. - filename; - } - fprintf(out, "0x%08x %s\n", - MAKE_RESOURCE(i + 1, - h->package_list[i].type_list[j].type, - h->package_list[i].type_list[j].first_instance + k), - filename); + packdef_instance *instance = &type->instances[k]; + if (!h->packaged) + filename = (instance->filename == NULL) ? + "<>" : instance->filename; + fprintf(out, "0x%08x %s\n", MAKE_RESOURCE(i + 1, type->type, + type->first_instance + k), filename); } // for k } // for j } // for i @@ -445,8 +524,8 @@ listResources(const index_header *h, const uint8 *buf, FILE *out) { } char * -get_file_name(const uint8 *buf, const index_header *h, uint16 file_index) { - static char result[MAXPATHLEN]; +getFileName(const uint8 *buf, const index_header *h, uint16 file_index) { + static char result[PATH_MAX]; char *ptr; file_info *fi; uint16 path_offset; @@ -484,7 +563,7 @@ writeFile(const char *file, const uint8 *data, size_t len) { // check if all the path components exist { const char *ptr; - char path[MAXPATHLEN]; + char path[PATH_MAX]; ptr = file; if (ptr[0] == '/') @@ -515,10 +594,10 @@ writeFile(const char *file, const uint8 *data, size_t len) { ssize_t written; written = write(fd, data, len); if (written < 0) { - if (errno != EINTR) { - perror("write failed"); - exit(1); - } + if (errno == EINTR) + continue; + perror("write failed"); + exit(EXIT_FAILURE); } len -= written; data += written; @@ -526,43 +605,136 @@ writeFile(const char *file, const uint8 *data, size_t len) { close(fd); } +// If everything is ok, 0 is returned. +// Returns -1 on error, in which case errno is set. +int +copyFileSegment(const char *src, FILE *inFile, size_t offset, + const char *dest, size_t size) { + FILE *outFile; +#define BUFSIZE 4096 + uint8 buf[BUFSIZE]; + if (fseek(inFile, offset, SEEK_SET) == -1) { + int savedErrno = errno; + fprintf(stderr, "Seeking in input file %s (to %ld) failed: %s.\n", + src, (long int) offset, strerror(errno)); + errno = savedErrno; + return -1; + } + + outFile = fopen(dest, "wb"); + if (outFile == NULL) { + int savedErrno = errno; + fprintf(stderr, "Opening output file %s failed: %s.\n", + dest, strerror(errno)); + errno = savedErrno; + return -1; + } + + while (size > 0) { + size_t toRead; + size_t numRead; + size_t numWritten; + + toRead = sizeof buf; + if (toRead > size) + toRead = size; + + do { + numRead = fread(buf, 1, toRead, inFile); + } while (ferror(inFile) && errno == EINTR); + if (numRead != toRead) { + if (ferror(inFile)) { + int savedErrno = errno; + fprintf(stderr, "Error reading file %s: %s.\n", src, + strerror(errno)); + fclose(outFile); + errno = savedErrno; + return -1; + } else { + // EOF + assert(feof(inFile)); + fprintf(stderr, "Unexpected end-of-file while reading " + "file %s.\n", src); + fclose(outFile); + errno = EIO; + return -1; + } + } + + do { + numWritten = fwrite(buf, 1, numRead, outFile); + } while (ferror(outFile) && errno == EINTR); + if (numWritten != numRead) { + int savedErrno = errno; + fprintf(stderr, "Error writing to file %s: %s.\n", dest, + strerror(errno)); + fclose(outFile); + errno = savedErrno; + return -1; + } + + size -= numWritten; + } + + fclose(outFile); + return 0; +} + +// Only for packaged files. void writeFiles(const index_header *h, const uint8 *data, const char *path) { int i; - char filename[MAXPATHLEN]; + char filename[PATH_MAX]; int num_types, num_instances; for (i = 0; i < h->num_packages; i++) { + package_desc *package = &h->package_list[i]; int j; + FILE *inFile = NULL; + + if (package->filename != NULL) { + inFile = fopen(package->filename, "rb"); + if (inFile == NULL) { + fprintf(stderr, "Error: Could not open input file %s.\n", + package->filename); + continue; + } + } num_types = h->package_list[i].num_types; for (j = 0; j < num_types; j++) { + packtype_desc *type = &package->type_list[j]; int k; - num_instances = h->package_list[i].type_list[j].num_instances; + num_instances = type->num_instances; for (k = 0; k < num_instances; k++) { - sprintf(filename, "%s/%08x", - path, - MAKE_RESOURCE(i + 1, - h->package_list[i].type_list[j].type, - h->package_list[i].type_list[j].first_instance + k)); + packdef_instance *instance = &type->instances[k]; + sprintf(filename, "%s/%08x", path, + MAKE_RESOURCE(i + 1, type->type, + type->first_instance + k)); #if 0 sprintf(filename, "%s/%08x=%03x-%02x-%03x", path, - MAKE_RESOURCE(i + 1, - h->package_list[i].type_list[j].type, - h->package_list[i].type_list[j].first_instance + k), - i + 1, - h->package_list[i].type_list[j].type, - h->package_list[i].type_list[j].first_instance + k); + MAKE_RESOURCE(i + 1, type->type, + type->first_instance + k), i + 1, type->type, + type->first_instance + k); #endif - writeFile(filename, - &data[h->package_list[i].type_list[j].instances[k]. - offset], - h->package_list[i].type_list[j].instances[k].size); + if (package->filename != NULL) { + // Resource is contained in a different file. + copyFileSegment(instance->filename, inFile, + instance->offset, filename, instance->size); + } else { + // Resource is contained in this package file itself. + writeFile(filename, &data[instance->offset], + instance->size); + } } } + + if (inFile != NULL) + fclose(inFile); } } + diff --git a/tools/pkg/unpkg.h b/tools/pkg/unpkg.h index 999c9d601..4b6f96de4 100644 --- a/tools/pkg/unpkg.h +++ b/tools/pkg/unpkg.h @@ -13,6 +13,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include typedef unsigned char uint8; @@ -73,7 +74,7 @@ typedef struct { } package_desc; typedef struct { - uint8 res_flags; + bool packaged; uint32 packmem_list_offs; uint32 path_list_offs; uint32 file_list_offs;