summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cli.c9
-rw-r--r--src/cli.h4
-rw-r--r--src/compress.c2
-rw-r--r--src/io.c67
-rw-r--r--src/io.h22
-rw-r--r--src/main.c48
-rw-r--r--src/table.c37
7 files changed, 119 insertions, 70 deletions
diff --git a/src/cli.c b/src/cli.c
index 78e0d88..dbb2f4f 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -39,8 +39,6 @@ static void parse_output_path(const char *optarg, struct params *p) {
}
-/* Updates the game field of `p` to properly reflect the game ID
- specified by the string, `optarg`. */
static void parse_game_id(const char *optarg, struct params *p) {
if (!strcmp(optarg, "nekopara_volume_0"))
p->game = NEKOPARA_VOLUME_0;
@@ -53,7 +51,6 @@ static void parse_game_id(const char *optarg, struct params *p) {
}
-/* Returns a params structure parsed from `argv`. */
struct params parse_args(int argc, char **argv) {
/* getopt maintains external state which needs to be reset each time
`parse_args` is called, otherwise strange things will occur. */
@@ -127,9 +124,3 @@ struct params parse_args(int argc, char **argv) {
return p;
}
-
-
-/* Frees allocated memory associated with `p`. */
-void params_free(struct params p) {
- free(p.out);
-}
diff --git a/src/cli.h b/src/cli.h
index 73693d9..6594072 100644
--- a/src/cli.h
+++ b/src/cli.h
@@ -42,8 +42,4 @@ struct params {
int vararg_index; /* Start index of paths in argv. */
};
-/* Returns a params structure parsed from `argv`. */
struct params parse_args(int argc, char **argv);
-
-/* Frees allocated memory associated with `p`. */
-void params_free(struct params p);
diff --git a/src/compress.c b/src/compress.c
index 9b6607d..3137145 100644
--- a/src/compress.c
+++ b/src/compress.c
@@ -78,7 +78,7 @@ struct stream *stream_deflate(struct stream *s, size_t len) {
do {
strm.avail_out = len;
strm.next_out = (unsigned char *) new->_cur;
- deflate(&strm, Z_NO_FLUSH); // Flush param may cause issues.
+ deflate(&strm, Z_FINISH);
} while (strm.avail_out == 0);
new->len = len - strm.avail_out;
diff --git a/src/io.c b/src/io.c
index cd6e8a3..759902c 100644
--- a/src/io.c
+++ b/src/io.c
@@ -26,45 +26,70 @@
/* Allocates `len` bytes of non-zeroed memory and returns a new stream
- structure pointing to it. */
+ structure pointing to it, or NULL if any allocations fail. The stream
+ returned is guaranteed to be positioned at the beginning. */
struct stream *stream_new(size_t len) {
struct stream *new = malloc(sizeof(struct stream));
- new->len = len;
+ if (new == NULL)
+ return NULL;
+
new->_start = malloc(len);
- new->_cur = new->_start;
- new->_loc = HEAP;
+ if (new->_start == NULL)
+ return NULL;
+
+ new->_cur = new->_start;
+ new->len = len;
+ new->_loc = HEAP;
return new;
}
-/* Copies `n` bytes from `s` into a new stream structure. */
+/* Copies `n` bytes from `s` into a new stream structure, returning NULL
+ if any allocations fail. The stream returned is guaranteed to be
+ positioned at the beginning. */
struct stream *stream_clone(struct stream *s, size_t n) {
struct stream *new = stream_new(n);
+ if (new == NULL)
+ return NULL;
+
stream_write(new, s->_cur, n);
stream_rewind(new);
return new;
}
-/* Maps the file at the given `path` into a stream structure. */
+/* Maps the file at the given `path` into a stream structure, returning
+ NULL if the path does not exist or if any allocations fail. The
+ stream returned is guaranteed to be positioned at the beginning. */
struct stream *stream_from_file(char *path) {
FILE *fp = fopen(path, "rb");
- if (fp == NULL) return NULL;
+ if (fp == NULL)
+ return NULL;
+
struct stream *new = malloc(sizeof(struct stream));
+ if (new == NULL)
+ return NULL;
+
fseek(fp, 0, SEEK_END);
new->len = ftell(fp);
fseek(fp, 0, SEEK_SET);
+
new->_start = malloc(new->len);
- new->_cur = new->_start;
+ if (new->_start == NULL) {
+ free(new);
+ return NULL;
+ }
+
fread(new->_start, new->len, 1, fp);
+ new->_cur = new->_start;
new->_loc = HEAP;
fclose(fp);
return new;
}
-/* Called to free or unmap the memory chunk associated with the given
- stream, as well as the stream structure itself. */
+/* Called to free memory associated with the given stream, as well as
+ the stream structure itself. */
void stream_free(struct stream *s) {
switch (s->_loc) {
case HEAP:
@@ -82,12 +107,6 @@ void stream_read(void *dest, struct stream *s, size_t n) {
}
-/* Dumps the contents of `s` into the file specified by `fp`. */
-void stream_dump(FILE *fp, struct stream *s, size_t n) {
- fwrite(s->_cur, n, 1, fp);
-}
-
-
/* Copies `n` bytes into the given stream from the memory area specified
by `src`. The stream's cursor is advanced appropriately. */
void stream_write(struct stream *s, void *src, size_t n) {
@@ -105,12 +124,18 @@ void stream_write(struct stream *s, void *src, size_t n) {
}
-/* Concatenats the contents of `src` onto `dst`. */
+/* Concatenates the contents of `src` onto `dst`. */
void stream_concat(struct stream *dst, struct stream *src, size_t n) {
stream_write(dst, src->_cur, n);
}
+/* Dumps the contents of `s` into the file specified by `fp`. */
+void stream_dump(FILE *fp, struct stream *s, size_t n) {
+ fwrite(s->_cur, n, 1, fp);
+}
+
+
/* Applies an initial and primary key to the given stream, effectively
encrypting or decrypting it. */
void stream_xor(struct stream *s, uint8_t initial, uint8_t primary) {
@@ -125,10 +150,10 @@ size_t stream_tell(struct stream *s) {
}
-/* Sets the stream's position indicator to the given `pos`. If `whence`
- is set to SEEK_SET, SEEK_CUR, or SEEK_END, the offset is relative to
- the start of the file, the current position indicator, or
- end-of-file, respectively. */
+/* Sets the stream's position indicator to the given `pos`. A `whence`
+ value of SEEK_SET indicates seeking relative to the beginning of the
+ file, SEEK_CUR indicates seeking relative to the current position,
+ and SEEK_END indicates seeking from the end of the file. */
void stream_seek(struct stream *s, size_t pos, int whence) {
switch (whence) {
case SEEK_SET:
diff --git a/src/io.h b/src/io.h
index ae80149..c5c1f56 100644
--- a/src/io.h
+++ b/src/io.h
@@ -40,17 +40,22 @@ struct stream {
};
/* Allocates `len` bytes of non-zeroed memory and returns a new stream
- structure pointing to it. */
+ structure pointing to it, or NULL if any allocations fail. The stream
+ returned is guaranteed to be positioned at the beginning. */
struct stream *stream_new(size_t len);
-/* Copies `n` bytes from `s` into a new stream structure. */
+/* Copies `n` bytes from `s` into a new stream structure, returning NULL
+ if any allocations fail. The stream returned is guaranteed to be
+ positioned at the beginning. */
struct stream *stream_clone(struct stream *s, size_t n);
-/* Maps the file at the given `path` into a stream structure. */
+/* Maps the file at the given `path` into a stream structure, returning
+ NULL if the path does not exist or if any allocations fail. The
+ stream returned is guaranteed to be positioned at the beginning. */
struct stream *stream_from_file(char *path);
-/* Called to free or unmap the memory chunk associated with the given
- stream, as well as the stream structure itself. */
+/* Called to free memory associated with the given stream, as well as
+ the stream structure itself. */
void stream_free(struct stream *s);
/* Copies `n` bytes from the given stream into the memory area specified
@@ -61,7 +66,7 @@ void stream_read(void *dest, struct stream *s, size_t n);
by `src`. The stream's cursor is advanced appropriately. */
void stream_write(struct stream *s, void *src, size_t n);
-/* Concatenats the contents of `src` onto `dst`. */
+/* Concatenates the contents of `src` onto `dst`. */
void stream_concat(struct stream *dst, struct stream *src, size_t n);
/* Dumps the contents of `s` into the file specified by `fp`. */
@@ -74,7 +79,10 @@ void stream_xor(struct stream *s, uint8_t initial, uint8_t primary);
/* Obtains the current value of the stream's position indicator. */
size_t stream_tell(struct stream *s);
-/* Sets the stream's position indicator to the given `pos`. */
+/* Sets the stream's position indicator to the given `pos`. A `whence`
+ value of SEEK_SET indicates seeking relative to the beginning of the
+ file, SEEK_CUR indicates seeking relative to the current position,
+ and SEEK_END indicates seeking from the end of the file. */
void stream_seek(struct stream *s, size_t pos, int whence);
/* Sets the stream's position indicator to the beginning. */
diff --git a/src/main.c b/src/main.c
index 49fb944..47a7d17 100644
--- a/src/main.c
+++ b/src/main.c
@@ -17,6 +17,7 @@
You should have received a copy of the GNU General Public License
along with Nekopack. If not, see <http://www.gnu.org/licenses/>. */
+#include <errno.h>
#include <sys/stat.h>
#include <stdint.h>
#include <stdio.h>
@@ -33,23 +34,20 @@
#define EXIT_FAILURE 1
#define EXIT_SUCCESS 0
-#define VERSION_STR "2.1.0b1"
+#define NEKOPACK_VERSION "2.1.0b1"
-/* Writes usage information to stderr. */
static void print_usage(char *progn) {
fprintf(stderr, "Usage: %s [OPTIONS] (ARCHIVES) [PATHS]\n", progn);
}
-/* Writes versioning information to stdout. */
static void print_version(void) {
printf("Nekopack version %s\nProgrammed by Jakob. "
- "<http://jakob.space>\n", VERSION_STR);
+ "<http://jakob.space>\n", NEKOPACK_VERSION);
}
-/* Writes help information to stdout. */
static void print_help(void) {
printf("A tool for decompressing the XP3 archives used by Nekopara.\n\n"
" -h, --help\t\tDisplay this help page and exit.\n"
@@ -69,7 +67,6 @@ static void print_help(void) {
}
-/* Inflates the table according to information in the header. */
static struct stream *load_table(struct stream *s) {
uint8_t compressed;
uint64_t len, decompressed_len;
@@ -102,7 +99,7 @@ void make_dirs(char *path) {
}
-/* Concatenates `name` and the output path specified in `p`. */
+/* Joins `name` and the output path specified in `p`. */
static char *get_path(struct params p, char *name) {
size_t name_len = strlen(name);
char *path = malloc(p.out_len + name_len + 2);
@@ -112,7 +109,6 @@ static char *get_path(struct params p, char *name) {
}
-/* Prints the filename of the entry specified by `e` to stdout. */
static void list(struct table_entry *e) {
if (e->filename == NULL || e->segments == NULL) {
fprintf(stderr, "Unpaired entries found. Archive may be corrupted.\n");
@@ -122,7 +118,6 @@ static void list(struct table_entry *e) {
}
-/* Extracts the archive entry specified by `e` to disk. */
static void extract(struct stream *s, struct table_entry *e, struct params p) {
if (e->filename == NULL || e->segments == NULL) {
fprintf(stderr, "Unpaired entries found. Archive may be corrupted.\n");
@@ -172,15 +167,31 @@ static void extract(struct stream *s, struct table_entry *e, struct params p) {
static void map_entries(char *path, struct params p) {
struct stream *archive = stream_from_file(path);
if (archive == NULL) {
- perror(path);
+ if (errno == ENOENT) {
+ perror(path);
+ } else {
+ fprintf(stderr, "Error allocating memory.\n");
+ }
return;
}
struct header *h = read_header(archive);
+ if (h == NULL) {
+ fprintf(stderr, "File is not an XP3 archive.\n");
+ return;
+ }
stream_seek(archive, h->table_offset, SEEK_SET);
- struct stream *table = load_table(archive);
- struct table_entry *root = read_table(table);
+ struct stream *table = load_table(archive);
+ if (table == NULL) {
+ fprintf(stderr, "Error allocating memory.\n");
+ return;
+ }
+
+ struct table_entry *root = read_table(table);
+ if (root == NULL) {
+ fprintf(stderr, "Error allocating memory.\n");
+ }
for (struct table_entry *cur = root->next; cur != NULL; cur = cur->next) {
switch (p.mode) {
@@ -199,8 +210,9 @@ static void map_entries(char *path, struct params p) {
}
-/* Creates a new XP3 archive at the first path specified by `paths`, and
- flattens files specified by any following paths into the archive. */
+/* Creates a new XP3 archive. The destination of the archive is the
+ first string in `paths`, and the files at any following paths will be
+ flattened into the archive. */
static void create_archive(char **paths, int argc, struct params p) {
FILE *fp = fopen(paths[0], "wb+");
if (fp == NULL) {
@@ -237,7 +249,7 @@ static void create_archive(char **paths, int argc, struct params p) {
h->table_offset = 40 + data_size;
dump_table(table, root);
-
+
data_compressed = stream_deflate(data, data_size);
table_compressed = stream_deflate(table, table_size);
stream_free(data);
@@ -254,7 +266,7 @@ static void create_archive(char **paths, int argc, struct params p) {
fwrite(&compressed, sizeof(uint8_t), 1, fp);
fwrite(&len, sizeof(uint64_t), 1, fp);
fwrite(&decompressed_len, sizeof(uint64_t), 1, fp);
-
+
stream_dump(fp, table_compressed, table_size);
}
@@ -264,7 +276,7 @@ int main(int argc, char **argv) {
switch (p.mode) {
case USAGE:
print_usage(argv[0]);
- params_free(p);
+ free(p.out);
return EXIT_FAILURE;
case VERSION:
print_version();
@@ -280,6 +292,6 @@ int main(int argc, char **argv) {
case CREATE:
create_archive(argv + p.vararg_index, argc, p);
}
- params_free(p);
+ free(p.out);
return EXIT_SUCCESS;
}
diff --git a/src/table.c b/src/table.c
index 1bb14cc..7e11668 100644
--- a/src/table.c
+++ b/src/table.c
@@ -41,11 +41,14 @@
static void read_segm(struct stream *s, struct table_entry *tmp, uint64_t count) {
tmp->segment_count = count;
tmp->segments = malloc(sizeof(struct segment *) * count);
- if (tmp->segments == NULL) return;
+ if (tmp->segments == NULL)
+ return;
for (uint64_t i = 0; i < count; i++) {
tmp->segments[i] = malloc(sizeof(struct segment));
- if (tmp->segments[i] == NULL) return;
+ if (tmp->segments[i] == NULL)
+ return;
+
stream_read(&tmp->segments[i]->compressed, s, sizeof(uint32_t));
stream_read(&tmp->segments[i]->offset, s, sizeof(uint64_t));
stream_read(&tmp->segments[i]->decompressed_size, s, sizeof(uint64_t));
@@ -108,11 +111,14 @@ static void dump_time(struct stream *s, uint64_t timestamp) {
will be modified. Otherwise, a new entry will be created and appended
to the linked list. */
void read_file(struct stream *s, struct table_entry *root) {
- bool ended = false;
- uint32_t magic;
- uint64_t size;
+ bool ended = false;
+ uint32_t magic;
+ uint64_t size;
struct table_entry *cur, *tmp = calloc(sizeof(struct table_entry), 1);
+ if (tmp == NULL)
+ return;
+
do {
stream_read(&magic, s, sizeof(uint32_t));
stream_read(&size, s, sizeof(uint64_t));
@@ -137,6 +143,11 @@ void read_file(struct stream *s, struct table_entry *root) {
} while (!ended);
cur = get_node(root, tmp->key);
+ if (cur == NULL) {
+ free(tmp);
+ return;
+ }
+
cur->segment_count = tmp->segment_count;
cur->segments = tmp->segments;
cur->ctime = tmp->ctime;
@@ -179,14 +190,15 @@ void read_elif(struct stream *s, struct table_entry *root) {
name_len = name_len * 2 + 2;
cur = get_node(root, key);
- if (cur->filename != NULL) {
+ if (cur == NULL || cur->filename != NULL) {
return;
}
if (name_len < 0x100) {
buf = malloc(name_len);
name = malloc(name_len);
- if (buf == NULL || name == NULL) return;
+ if (buf == NULL || name == NULL)
+ return;
stream_read(buf, s, name_len);
utf16le_decode(buf, name, name_len);
@@ -199,12 +211,13 @@ void read_elif(struct stream *s, struct table_entry *root) {
}
name = tmp;
} else {
- /* strdup isn't defined in ISO/IEC 9899:1999 C. */
name = malloc(14);
- if (name == NULL) return;
+ if (name == NULL)
+ return;
strncpy(name, "COPYRIGHT.txt", 14);
stream_seek(s, name_len, SEEK_CUR);
}
+
cur->filename = name;
}
@@ -229,6 +242,8 @@ static void dump_elif(struct stream *s, struct table_entry *cur) {
in the archive's table section. */
struct table_entry *read_table(struct stream *s) {
struct table_entry *root = calloc(sizeof(struct table_entry), 1);
+ if (root == NULL)
+ return NULL;
bool ended = false;
uint32_t magic;
@@ -292,7 +307,9 @@ struct table_entry *get_node(struct table_entry *root, uint32_t key) {
for (cur = root; cur != NULL && cur->key != key; cur = cur->next);
if (cur == NULL) {
cur = calloc(sizeof(struct table_entry), 1);
- if (cur == NULL) return NULL;
+ if (cur == NULL)
+ return NULL;
+
entry_append(root, cur);
cur->key = key;
}