Skip to content
This repository has been archived by the owner on Jan 21, 2019. It is now read-only.

Commit

Permalink
Fixed segmentation fault on invalid paths, cleaned up code
Browse files Browse the repository at this point in the history
  • Loading branch information
TsarFox committed Apr 1, 2017
1 parent 06ef3f1 commit 5ac05ed
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 76 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
bin/
obj/
TAGS
GPATH
GRTAGS
GTAGS
50 changes: 32 additions & 18 deletions src/cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,46 @@
#include <getopt.h>

#include "cli.h"
#include "crypto.h" /* New, maybe clean up the section on game crypto? */
#include "crypto.h"


/* Copies `optarg` into the out field of `p` and ensures that it ends in
a trailing path delimiter. */
static void parse_output_path(const char *optarg, struct params *p) {
p->out_len = strlen(optarg);
p->out = malloc(p->out_len + 2);
strcpy(p->out, optarg);
if (p->out[p->out_len - 1] != '/') {
p->out[p->out_len] = '/';
p->out_len += 1;
}
}


/* 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;
else if (!strcmp(optarg, "nekopara_volume_0_steam"))
p->game = NEKOPARA_VOLUME_0_STEAM;
else if (!strcmp(optarg, "nekopara_volume_1"))
p->game = NEKOPARA_VOLUME_1;
else if (!strcmp(optarg, "nekopara_volume_1_steam"))
p->game = NEKOPARA_VOLUME_1_STEAM;
}


/* Returns a params structure parsed from `argv`. */
struct params parse_args(int argc, char **argv) {
struct params p = {0};

if (argc < 2) {
p.mode = USAGE;
return p;
}
p.mode = EXTRACT;

int cur = 0, opt_index = 0, count = 0;
static struct option long_opts[] = {
{"help", no_argument, NULL, 'h'},
Expand Down Expand Up @@ -63,24 +90,11 @@ struct params parse_args(int argc, char **argv) {
break;
case 'o':
count++;
p.out_len = strlen(optarg);
p.out = malloc(p.out_len + 2);
strcpy(p.out, optarg);
if (p.out[p.out_len - 1] != '/') {
p.out[p.out_len] = '/';
p.out_len += 1;
}
parse_output_path(optarg, &p);
break;
case 'g':
count++;
if (!strcmp(optarg, "nekopara_volume_0"))
p.game = NEKOPARA_VOLUME_0;
else if (!strcmp(optarg, "nekopara_volume_0_steam"))
p.game = NEKOPARA_VOLUME_0_STEAM;
else if (!strcmp(optarg, "nekopara_volume_1"))
p.game = NEKOPARA_VOLUME_1;
else if (!strcmp(optarg, "nekopara_volume_1_steam"))
p.game = NEKOPARA_VOLUME_1_STEAM;
parse_game_id(optarg, &p);
case 'e':
p.mode = EXTRACT;
break;
Expand Down
10 changes: 5 additions & 5 deletions src/cli.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ enum {

/* Structure for storing options set from the command-line. */
struct params {
int mode; /* Current mode of operation. */
int game; /* Which encryption keys to use. */
bool verbose; /* Whether or not to output progress messages. */
int mode; /* Current mode of operation. */
int game; /* Which encryption keys to use. */
bool verbose; /* Whether or not to output progress messages. */
int vararg_index; /* Start index of paths in argv. */
char *out; /* Path to extract files to. */
size_t out_len; /* Length of the output path string. */
char *out; /* Path to extract files to. */
size_t out_len; /* Length of the output path string. */
};

/* Returns a params structure parsed from `argv`. */
Expand Down
4 changes: 2 additions & 2 deletions src/crypto.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ enum {

/* Structure containing game-specific encryption information. */
struct game_key {
bool uses_initial; /* Whether or not to use an initial key. */
uint32_t master; /* Master key specific to the game. */
bool uses_initial; /* Whether or not to use an initial key. */
uint32_t master; /* Master key specific to the game. */
uint8_t fallback_initial; /* Fallback if initial key is 0. */
uint8_t fallback_primary; /* Fallback if primary key is 0. */
};
Expand Down
10 changes: 5 additions & 5 deletions src/header.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@

/* Structure representing the header section of an XP3 archive. */
struct header {
char magic[11]; /* Identifier for the archive. */
uint64_t info_offset; /* Offset to `table_size`. */
uint32_t version; /* Raw value containing the archive version. */
uint64_t table_size; /* The size of the table section. (?) */
uint8_t flags; /* A flags variable for the archive. (?) */
char magic[11]; /* Identifier for the archive. */
uint64_t info_offset; /* Offset to `table_size`. */
uint32_t version; /* Raw value containing the archive version. */
uint64_t table_size; /* The size of the table section. (?) */
uint8_t flags; /* A flags variable for the archive. (?) */
uint64_t table_offset; /* Offset to the archive table. */
};

Expand Down
6 changes: 3 additions & 3 deletions src/io.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ enum _location {
position. The structure additionally maintains a `length` variable
containing the stream's length. */
struct stream {
size_t len; /* Length of the stream. */
size_t len; /* Length of the stream. */
char *_start; /* Pointer to the stream's start. */
char *_cur; /* Pointer to the current position. */
enum _location _loc; /* Location of the stream in memory. */
char *_cur; /* Pointer to the current position. */
enum _location _loc; /* Location of the stream in memory. */
};

/* Allocates `len` bytes of non-zeroed memory and returns a new stream
Expand Down
67 changes: 33 additions & 34 deletions src/main.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* main.c -- Entry point to the program.
/* main.c -- Command-line entry point to the Nekopack.
Copyright (C) 2017 Jakob Kreuze, All Rights Reserved.
Expand Down Expand Up @@ -33,12 +33,7 @@
#define EXIT_FAILURE 1
#define EXIT_SUCCESS 0

#define VERSION_STR "2.0.0a2"


/* Pointer to a function to be mapped to entries in the table. */
typedef void (*mapfn)(struct stream *archive, struct table_entry *e,
struct params p);
#define VERSION_STR "2.0.0"


/* Writes usage information to stderr. */
Expand All @@ -65,7 +60,7 @@ static void print_help(void) {
" -l, --list\t\tList the contents of the archive.\n\n"
" -o, --output\t\tPath to extract files to.\n"
" -g, --game\t\tGame the archive is from. Required for file "
"decryption\n"
"decryption.\n"
" -q, --quiet\t\tDon't display information about extracted "
"files.\n\n"
"Supported games: nekopara_volume_0, nekopara_volume_0_steam,\n"
Expand All @@ -88,10 +83,9 @@ static struct stream *load_table(struct stream *s) {

/* Creates any directories that do not already exist in `path`. */
void make_dirs(char *path) {
struct stat tmp = {0};
char *buf = calloc(0x100, 1);
char *buf = calloc(0x100, 1);
char *buf_start = buf;

struct stat tmp = {0};
for (int i = 0; i < 0x100 && path[i] != '\0'; i++) {
if (path[i] == '/') {
*buf++ = '/';
Expand All @@ -116,26 +110,20 @@ static char *get_path(struct params p, char *name) {
}


/* Basic mapfn for printing the filename of each entry. */
static void list(struct stream *s, struct table_entry *e, struct params p) {
if (e->filename == NULL) {
fprintf(stderr, "Found File entry without matching eliF. Archive may "
"be corrupted.\n");
/* 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, "Inconsistency detected. Archive may be corrupted.\n");
return;
}
printf("%s\n", e->filename);
}


/* Mapfn for extracting the contents of the archive. */
/* 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) {
fprintf(stderr, "Found File entry without matching eliF. Archive may "
"be corrupted.\n");
return;
} else if (e->segments == NULL) {
fprintf(stderr, "Found eliF entry without matching File. Archive may "
"be corrupted.\n");
if (e->filename == NULL || e->segments == NULL) {
fprintf(stderr, "Inconsistency detected. Archive may be corrupted.\n");
return;
}

Expand Down Expand Up @@ -177,17 +165,30 @@ static void extract(struct stream *s, struct table_entry *e, struct params p) {
}


/* Maps `fn` to every table entry found in the archive at `path`. */
static void map_entries(char *path, struct params p, mapfn fn) {
/* Loads an XP3 archive and maps the function associated to the current
mode of operation to every entry. */
static void map_entries(char *path, struct params p) {
struct stream *archive = stream_from_file(path);
struct header *h = read_header(archive);
if (archive == NULL) {
perror(path);
return;
}

struct header *h = read_header(archive);
stream_seek(archive, h->table_offset, SEEK_SET);

struct stream *table = load_table(archive);
struct table_entry *root = read_table(table);

for (struct table_entry *cur = root->next; cur != NULL; cur = cur->next)
fn(archive, cur, p);
for (struct table_entry *cur = root->next; cur != NULL; cur = cur->next) {
switch (p.mode) {
case LIST:
list(cur);
break;
case EXTRACT:
extract(archive, cur, p);
}
}

entry_free(root);
stream_free(table);
Expand All @@ -201,20 +202,18 @@ int main(int argc, char **argv) {
switch (p.mode) {
case USAGE:
print_usage(argv[0]);
return 1;
params_free(p);
return EXIT_FAILURE;
case VERSION:
print_version();
break;
case HELP:
print_help();
break;
case LIST:
for (int i = p.vararg_index; i < argc; i++)
map_entries(argv[i], p, list);
break;
case EXTRACT:
for (int i = p.vararg_index; i < argc; i++)
map_entries(argv[i], p, extract);
map_entries(argv[i], p);
}
params_free(p);
return EXIT_SUCCESS;
Expand Down
16 changes: 8 additions & 8 deletions src/table.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,20 @@

/* Structure representing one segment associated with a File entry. */
struct segment {
bool compressed; /* Whether or not the segment is compressed. */
uint64_t offset; /* Offset to the segment's beginning. */
uint64_t compressed_size; /* Size of compressed segment. */
bool compressed; /* Whether or not the segment is compressed. */
uint64_t offset; /* Offset to the segment's beginning. */
uint64_t compressed_size; /* Size of compressed segment. */
uint64_t decompressed_size; /* Size of decompressed segment. */
};

/* Structure representing an entry in the archive's table section. */
struct table_entry {
uint32_t key; /* File-specific key for encryption. */
uint64_t ctime; /* Timestamp of creation time. */
uint32_t key; /* File-specific key for encryption. */
uint64_t ctime; /* Timestamp of creation time. */
uint64_t segment_count; /* Number of segments. */
struct segment **segments; /* Array of associated segments. */
char *filename; /* String containing file's name. */
struct table_entry *next; /* Pointer to the next entry. */
char *filename; /* String containing file's name. */
struct segment **segments; /* Array of associated segments. */
struct table_entry *next; /* Pointer to the next entry. */
};


Expand Down

0 comments on commit 5ac05ed

Please sign in to comment.