Support for SC1 format.

git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@2727 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
Meep-Eep
2007-04-08 04:08:48 +00:00
parent bdd3d90d66
commit b462ad8ad1
2 changed files with 309 additions and 136 deletions
+285 -113
View File
@@ -19,6 +19,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <assert.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <sys/mman.h> #include <sys/mman.h>
@@ -37,12 +38,18 @@ struct options {
char list; char list;
char print; char print;
char verbose; 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); index_header *readIndex(const struct options *opts, const uint8 *buf);
void printIndex(const index_header *h, const uint8 *buf, FILE *out); void printIndex(const struct options *opts, const index_header *h,
char *get_file_name(const uint8 *buf, const index_header *h, const uint8 *buf, FILE *out);
char *getFileName(const uint8 *buf, const index_header *h,
uint16 file_index); uint16 file_index);
void writeFiles(const index_header *h, const uint8 *data, const char *path); void writeFiles(const index_header *h, const uint8 *data, const char *path);
void parse_arguments(int argc, char *argv[], struct options *opts); void parse_arguments(int argc, char *argv[], struct options *opts);
@@ -78,9 +85,9 @@ main(int argc, char *argv[]) {
} }
close(in); close(in);
h = readIndex(buf); h = readIndex(&opts, buf);
if (opts.print) if (opts.print)
printIndex(h, buf, stdout); printIndex(&opts, h, buf, stdout);
if (opts.list) if (opts.list)
listResources(h, buf, stdout); listResources(h, buf, stdout);
@@ -89,8 +96,8 @@ main(int argc, char *argv[]) {
size_t len; size_t len;
struct stat sb; struct stat sb;
if (!h->res_flags) { if (!h->packaged) {
fprintf(stderr, "Error: Cannot unpack data from unpackaged " fprintf(stderr, "Error: Cannot unpack data from non-packaged "
"file.\n"); "file.\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@@ -125,7 +132,12 @@ main(int argc, char *argv[]) {
void void
usage() { usage() {
fprintf(stderr, "unpkg [-o outputdir] [-v] [-l listfile] <infile>\n"); fprintf(stderr, "unpkg [-o <outputdir>] [-l] [-p] [-v] <infile>\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 void
@@ -133,8 +145,9 @@ parse_arguments(int argc, char *argv[], struct options *opts) {
char ch; char ch;
memset(opts, '\0', sizeof (struct options)); memset(opts, '\0', sizeof (struct options));
opts->type = Type_3do;
while (1) { while (1) {
ch = getopt(argc, argv, "hlo:pv"); ch = getopt(argc, argv, "hlo:pt:v");
if (ch == -1) if (ch == -1)
break; break;
switch(ch) { switch(ch) {
@@ -147,6 +160,25 @@ parse_arguments(int argc, char *argv[], struct options *opts) {
case 'p': case 'p':
opts->print = 1; opts->print = 1;
break; 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': case 'v':
opts->verbose = 1; opts->verbose = 1;
break; break;
@@ -167,12 +199,30 @@ parse_arguments(int argc, char *argv[], struct options *opts) {
} }
index_header * index_header *
readIndex(const uint8 *buf) { readIndex(const struct options *opts, const uint8 *buf) {
index_header *h; index_header *h;
uint16 res_flags;
const uint8 *bufptr; const uint8 *bufptr;
h = malloc(sizeof (index_header)); h = malloc(sizeof (index_header));
h->res_flags = MAKE_WORD(buf[0x00], buf[0x01]) ? 1 : 0; 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 = h->packmem_list_offs =
MAKE_DWORD(buf[0x02], buf[0x03], buf[0x04], buf[0x05]); MAKE_DWORD(buf[0x02], buf[0x03], buf[0x04], buf[0x05]);
h->path_list_offs = h->path_list_offs =
@@ -181,7 +231,10 @@ readIndex(const uint8 *buf) {
MAKE_DWORD(buf[0x0a], buf[0x0b], buf[0x0c], buf[0x0d]); MAKE_DWORD(buf[0x0a], buf[0x0b], buf[0x0c], buf[0x0d]);
h->num_packages = MAKE_WORD(buf[0x0e], buf[0x0f]); h->num_packages = MAKE_WORD(buf[0x0e], buf[0x0f]);
h->num_types = MAKE_WORD(buf[0x10], buf[0x11]); h->num_types = MAKE_WORD(buf[0x10], buf[0x11]);
h->header_len = MAKE_DWORD(buf[0x12], buf[0x13], buf[0x14], buf[0x15]); h->header_len = MAKE_DWORD(
buf[0x12], buf[0x13], buf[0x14], buf[0x15]);
break;
}
bufptr = buf + 0x16; bufptr = buf + 0x16;
h->package_list = malloc(h->num_packages * sizeof (package_desc)); h->package_list = malloc(h->num_packages * sizeof (package_desc));
@@ -221,65 +274,72 @@ readIndex(const uint8 *buf) {
{ {
int i, j; int i, j;
int num_types, num_instances; int num_types, num_instances;
uint32 temp;
for (i = 0; i < h->num_packages; i++) { for (i = 0; i < h->num_packages; i++) {
package_desc *package = &h->package_list[i];
uint32 next_offset; uint32 next_offset;
if (h->res_flags) { if (h->packaged) {
// packaged // One file name per package
char *temp; char *temp;
temp = get_file_name(buf, h, h->package_list[i].file_index); temp = getFileName(buf, h, package->file_index);
h->package_list[i].filename = package->filename = (temp == NULL) ? NULL : strdup(temp);
(temp == NULL) ? NULL : strdup(temp);
} else { } else {
// not packaged // File name is specified per resource instance.
h->package_list[i].filename = NULL; package->filename = NULL;
} }
num_types = h->package_list[i].num_types; num_types = package->num_types;
h->package_list[i].type_list = package->type_list = malloc(num_types * sizeof (packtype_desc));
malloc(num_types * sizeof (packtype_desc));
for (j = 0; j < num_types; j++) { 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]); temp = MAKE_DWORD(bufptr[0], bufptr[1], bufptr[2], bufptr[3]);
h->package_list[i].type_list[j].type = GET_TYPE(temp); type->type = GET_TYPE(temp);
h->package_list[i].type_list[j].first_instance = type->first_instance = GET_INSTANCE(temp);
GET_INSTANCE(temp); type->num_instances = GET_PACKAGE(temp);
h->package_list[i].type_list[j].num_instances =
GET_PACKAGE(temp);
bufptr += 4; bufptr += 4;
} }
if (h->res_flags) { next_offset = package->data_loc;
// packaged
next_offset = h->package_list[i].data_loc;
}
for (j = 0; j < num_types; j++) { for (j = 0; j < num_types; j++) {
packtype_desc *type = &package->type_list[j];
int k; int k;
num_instances = h->package_list[i].type_list[j].num_instances; num_instances = type->num_instances;
h->package_list[i].type_list[j].instances = type->instances =
malloc(sizeof (packdef_instance) * num_instances); malloc(sizeof (packdef_instance) * num_instances);
for (k = 0; k < num_instances; k++) { for (k = 0; k < num_instances; k++) {
if (h->res_flags) { packdef_instance *instance = &type->instances[k];
// packaged
h->package_list[i].type_list[j].instances[k].size = 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]); 4 * MAKE_WORD(bufptr[0], bufptr[1]);
h->package_list[i].type_list[j].instances[k].offset = break;
next_offset; }
h->package_list[i].type_list[j].instances[k]. instance->offset = next_offset;
filename = NULL; next_offset += instance->size;
next_offset += h->package_list[i].type_list[j]. instance->filename = NULL;
instances[k].size; // File name is specified per package.
} else { } else {
char *temp; char *temp;
h->package_list[i].type_list[j].instances[k].size = 0; instance->size = 0;
h->package_list[i].type_list[j].instances[k].offset = instance->offset = 0;
0; temp = getFileName(buf, h,
temp = get_file_name(buf, h,
MAKE_WORD(bufptr[0], bufptr[1])); MAKE_WORD(bufptr[0], bufptr[1]));
h->package_list[i].type_list[j].instances[k]. instance->filename = (temp == NULL) ?
filename = (temp == NULL) ?
NULL : strdup(temp); NULL : strdup(temp);
} }
bufptr += 2; bufptr += 2;
} // for k } // for k
@@ -290,11 +350,33 @@ readIndex(const uint8 *buf) {
} }
void 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; const uint8 *bufptr;
fprintf(out, "0x00000000 File type: %s\n", fprintf(out, "0x00000000 File type: %s\n",
h->res_flags ? "packaged" : "not packaged"); 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", fprintf(out, "0x00000002 packmem_list_offs: 0x%08x\n",
h->packmem_list_offs); h->packmem_list_offs);
fprintf(out, "0x00000006 path_list_offs: 0x%08x\n", fprintf(out, "0x00000006 path_list_offs: 0x%08x\n",
@@ -307,6 +389,8 @@ printIndex(const index_header *h, const uint8 *buf, FILE *out) {
h->num_types); h->num_types);
fprintf(out, "0x00000012 Header length: 0x%08x\n", fprintf(out, "0x00000012 Header length: 0x%08x\n",
h->header_len); h->header_len);
break;
}
bufptr = buf + 0x16; bufptr = buf + 0x16;
fprintf(out, "0x00000016 package info:\n"); 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; int num_types, num_instances;
for (i = 0; i < h->num_packages; i++) { for (i = 0; i < h->num_packages; i++) {
if (h->res_flags) { package_desc *package = &h->package_list[i];
// packaged
if (h->packaged) {
fprintf(out, "0x%08x Package #%d (%s):\n", fprintf(out, "0x%08x Package #%d (%s):\n",
bufptr - buf, i + 1, bufptr - buf, i + 1, (package->filename == NULL) ?
(h->package_list[i].filename == NULL) ? "<<INTERNAL>>" : package->filename);
"<<UNNAMED>>" : h->package_list[i].filename);
} else { } else {
// not packaged
fprintf(out, "0x%08x Package #%d:\n", fprintf(out, "0x%08x Package #%d:\n",
bufptr - buf, i + 1); bufptr - buf, i + 1);
} }
num_types = h->package_list[i].num_types; num_types = package->num_types;
for (j = 0; j < num_types; j++) { for (j = 0; j < num_types; j++) {
fprintf(out, "0x%08x Type #%d: type %d, " fprintf(out, "0x%08x Type #%d: type %d, "
"%d instances starting with #%d\n", "%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++) { for (j = 0; j < num_types; j++) {
packtype_desc *type = &package->type_list[j];
int k; 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++) { for (k = 0; k < num_instances; k++) {
if (h->res_flags) { packdef_instance *instance = &type->instances[k];
// packaged
if (h->packaged) {
fprintf(out, "0x%08x Instance #%d of " fprintf(out, "0x%08x Instance #%d of "
"type %d: size %d bytes\n", bufptr - buf, "type %d: size %d bytes\n", bufptr - buf,
k + h->package_list[i].type_list[j]. k + type->first_instance, type->type,
first_instance, instance->size);
h->package_list[i].type_list[j].type,
h->package_list[i].type_list[j].instances[k].
size);
} else { } else {
fprintf(out, "0x%08x Instance #%d of " fprintf(out, "0x%08x Instance #%d of "
"type %d: %s\n", bufptr - buf, "type %d: %s\n", bufptr - buf,
k + h->package_list[i].type_list[j]. k + type->first_instance, type->type,
first_instance, (instance->filename == NULL) ?
h->package_list[i].type_list[j].type, "<<UNNAMED>>" : instance->filename);
(h->package_list[i].type_list[j].instances[k]. // NB. NULL filenames should not occur for
filename == NULL) ? "<<UNNAMED>>" : // non-packaged files.
h->package_list[i].type_list[j].instances[k].
filename);
} }
bufptr += 2; bufptr += 2;
} // for k } // for k
} // for j } // for j
@@ -415,29 +496,27 @@ listResources(const index_header *h, const uint8 *buf, FILE *out) {
char *filename; char *filename;
for (i = 0; i < h->num_packages; i++) { for (i = 0; i < h->num_packages; i++) {
if (h->res_flags) { package_desc *package = &h->package_list[i];
// packaged
filename = (h->package_list[i].filename == NULL) ? if (h->packaged) {
"<<UNNAMED>>" : h->package_list[i].filename; // One filename for all resources in a package.
filename = (package->filename == NULL) ?
"<<INTERNAL>>" : package->filename;
} }
num_types = h->package_list[i].num_types; num_types = package->num_types;
for (j = 0; j < num_types; j++) { for (j = 0; j < num_types; j++) {
packtype_desc *type = &package->type_list[j];
int k; 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++) { for (k = 0; k < num_instances; k++) {
if (!h->res_flags) { packdef_instance *instance = &type->instances[k];
filename = (h->package_list[i].type_list[j].instances[k]. if (!h->packaged)
filename == NULL) ? "<<UNNAMED>>" : filename = (instance->filename == NULL) ?
h->package_list[i].type_list[j].instances[k]. "<<UNNAMED>>" : instance->filename;
filename; fprintf(out, "0x%08x %s\n", MAKE_RESOURCE(i + 1, type->type,
} type->first_instance + 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);
} // for k } // for k
} // for j } // for j
} // for i } // for i
@@ -445,8 +524,8 @@ listResources(const index_header *h, const uint8 *buf, FILE *out) {
} }
char * char *
get_file_name(const uint8 *buf, const index_header *h, uint16 file_index) { getFileName(const uint8 *buf, const index_header *h, uint16 file_index) {
static char result[MAXPATHLEN]; static char result[PATH_MAX];
char *ptr; char *ptr;
file_info *fi; file_info *fi;
uint16 path_offset; uint16 path_offset;
@@ -484,7 +563,7 @@ writeFile(const char *file, const uint8 *data, size_t len) {
// check if all the path components exist // check if all the path components exist
{ {
const char *ptr; const char *ptr;
char path[MAXPATHLEN]; char path[PATH_MAX];
ptr = file; ptr = file;
if (ptr[0] == '/') if (ptr[0] == '/')
@@ -515,10 +594,10 @@ writeFile(const char *file, const uint8 *data, size_t len) {
ssize_t written; ssize_t written;
written = write(fd, data, len); written = write(fd, data, len);
if (written < 0) { if (written < 0) {
if (errno != EINTR) { if (errno == EINTR)
continue;
perror("write failed"); perror("write failed");
exit(1); exit(EXIT_FAILURE);
}
} }
len -= written; len -= written;
data += written; data += written;
@@ -526,43 +605,136 @@ writeFile(const char *file, const uint8 *data, size_t len) {
close(fd); 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 void
writeFiles(const index_header *h, const uint8 *data, const char *path) { writeFiles(const index_header *h, const uint8 *data, const char *path) {
int i; int i;
char filename[MAXPATHLEN]; char filename[PATH_MAX];
int num_types, num_instances; int num_types, num_instances;
for (i = 0; i < h->num_packages; i++) { for (i = 0; i < h->num_packages; i++) {
package_desc *package = &h->package_list[i];
int j; 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; num_types = h->package_list[i].num_types;
for (j = 0; j < num_types; j++) { for (j = 0; j < num_types; j++) {
packtype_desc *type = &package->type_list[j];
int k; 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++) { for (k = 0; k < num_instances; k++) {
sprintf(filename, "%s/%08x", packdef_instance *instance = &type->instances[k];
path, sprintf(filename, "%s/%08x", path,
MAKE_RESOURCE(i + 1, MAKE_RESOURCE(i + 1, type->type,
h->package_list[i].type_list[j].type, type->first_instance + k));
h->package_list[i].type_list[j].first_instance + k));
#if 0 #if 0
sprintf(filename, "%s/%08x=%03x-%02x-%03x", sprintf(filename, "%s/%08x=%03x-%02x-%03x",
path, path,
MAKE_RESOURCE(i + 1, MAKE_RESOURCE(i + 1, type->type,
h->package_list[i].type_list[j].type, type->first_instance + k), i + 1, type->type,
h->package_list[i].type_list[j].first_instance + k), type->first_instance + k);
i + 1,
h->package_list[i].type_list[j].type,
h->package_list[i].type_list[j].first_instance + k);
#endif #endif
writeFile(filename, if (package->filename != NULL) {
&data[h->package_list[i].type_list[j].instances[k]. // Resource is contained in a different file.
offset], copyFileSegment(instance->filename, inFile,
h->package_list[i].type_list[j].instances[k].size); 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);
}
} }
+2 -1
View File
@@ -13,6 +13,7 @@
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/ */
#include <stdbool.h>
#include <stdint.h> #include <stdint.h>
typedef unsigned char uint8; typedef unsigned char uint8;
@@ -73,7 +74,7 @@ typedef struct {
} package_desc; } package_desc;
typedef struct { typedef struct {
uint8 res_flags; bool packaged;
uint32 packmem_list_offs; uint32 packmem_list_offs;
uint32 path_list_offs; uint32 path_list_offs;
uint32 file_list_offs; uint32 file_list_offs;