Compare commits

...
4 Commits
6 changed files with 361 additions and 244 deletions
+3 -1
View File
@@ -6,7 +6,9 @@ BIN_DIR = bin
LIB_NAME = fpk
STATIC_LIB = lib$(LIB_NAME).a
SRCS = src/fpk.c
SRCS = \
src/fpk.c \
src/fpk_dir.c
TOOL_SRCS = \
tools/fpack.c \
+4
View File
@@ -56,6 +56,10 @@ int fpk_writer_add(struct fpk_writer *w, const char *path, const void *data,
int fpk_writer_finalize(struct fpk_writer *w);
void fpk_writer_close(struct fpk_writer *w);
typedef int (*fpk_add_dir_cb)(const char *path, uint64_t size, void *ctx);
int fpk_writer_add_dir(struct fpk_writer *w, const char *dir_path,
fpk_add_dir_cb on_add, void *ctx);
#ifdef __cplusplus
}
#endif
+1 -1
View File
@@ -2,7 +2,7 @@
#define FPK_VERSION_H
#define FPK_VERSION_MAJOR 0
#define FPK_VERSION_MINOR 1
#define FPK_VERSION_MINOR 2
#define FPK_VERSION_PATCH 0
#define FPK_STRINGIFY_HELPER(x) #x
+289
View File
@@ -0,0 +1,289 @@
#if !defined(_WIN32)
#define _FILE_OFFSET_BITS 64
#define _POSIX_C_SOURCE 200809L
#endif
#include "fpk.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <windows.h>
#include <wchar.h>
#define fpk_dir_fseek64(f, off, whence) _fseeki64((f), (off), (whence))
#define fpk_dir_ftell64(f) _ftelli64((f))
#else
#include <dirent.h>
#include <sys/stat.h>
#define fpk_dir_fseek64(f, off, whence) fseeko((f), (off_t)(off), (whence))
#define fpk_dir_ftell64(f) ftello((f))
#endif
#ifdef _WIN32
#define IS_PATH_SEP(c) ((c) == '/' || (c) == '\\')
#else
#define IS_PATH_SEP(c) ((c) == '/')
#endif
#ifdef _WIN32
static wchar_t *utf8_to_wide(const char *s)
{
int wlen = MultiByteToWideChar(CP_UTF8, 0, s, -1, NULL, 0);
wchar_t *w;
if (wlen <= 0)
return NULL;
w = malloc((size_t)wlen * sizeof(wchar_t));
if (!w)
return NULL;
MultiByteToWideChar(CP_UTF8, 0, s, -1, w, wlen);
return w;
}
static char *wide_to_utf8(const wchar_t *w)
{
int len = WideCharToMultiByte(CP_UTF8, 0, w, -1, NULL, 0, NULL, NULL);
char *s;
if (len <= 0)
return NULL;
s = malloc((size_t)len);
if (!s)
return NULL;
WideCharToMultiByte(CP_UTF8, 0, w, -1, s, len, NULL, NULL);
return s;
}
#endif
static char *dup_str(const char *s)
{
size_t len = strlen(s) + 1;
char *out = malloc(len);
if (out != NULL)
memcpy(out, s, len);
return out;
}
static char *join_path(const char *dir, const char *name)
{
size_t dir_len = strlen(dir);
size_t name_len = strlen(name);
char *out = malloc(dir_len + 1 + name_len + 1);
if (out == NULL)
return NULL;
memcpy(out, dir, dir_len);
out[dir_len] = '/';
memcpy(out + dir_len + 1, name, name_len);
out[dir_len + 1 + name_len] = '\0';
return out;
}
static void normalize_path(char *s)
{
for (; *s; s++) {
if (*s == '\\')
*s = '/';
}
}
static void *read_file(const char *path, size_t *out_size)
{
FILE *fp;
int64_t len;
void *buf;
#ifdef _WIN32
wchar_t *wpath = utf8_to_wide(path);
if (!wpath)
return NULL;
fp = _wfopen(wpath, L"rb");
free(wpath);
#else
fp = fopen(path, "rb");
#endif
if (!fp)
return NULL;
if (fpk_dir_fseek64(fp, 0, SEEK_END) != 0) {
fclose(fp);
return NULL;
}
len = fpk_dir_ftell64(fp);
if (len < 0 || fpk_dir_fseek64(fp, 0, SEEK_SET) != 0) {
fclose(fp);
return NULL;
}
buf = malloc((size_t)len);
if (!buf) {
fclose(fp);
return NULL;
}
if (fread(buf, 1, (size_t)len, fp) != (size_t)len) {
free(buf);
fclose(fp);
return NULL;
}
fclose(fp);
*out_size = (size_t)len;
return buf;
}
static int add_file(struct fpk_writer *w, const char *path, size_t base_len,
fpk_add_dir_cb on_add, void *ctx)
{
const char *rel_path = path + base_len;
void *data;
size_t size;
char *norm;
int ret;
while (*rel_path == '/')
rel_path++;
norm = dup_str(rel_path);
if (norm == NULL)
return 0;
normalize_path(norm);
data = read_file(path, &size);
if (data == NULL) {
free(norm);
return 0;
}
if (fpk_writer_add(w, norm, data, size) != 0) {
free(data);
free(norm);
return 0;
}
ret = on_add == NULL || on_add(norm, (uint64_t)size, ctx) != 0;
free(data);
free(norm);
return ret;
}
#ifdef _WIN32
static int add_dir(struct fpk_writer *w, const char *dir, size_t base_len,
fpk_add_dir_cb on_add, void *ctx)
{
char *pattern = join_path(dir, "*");
wchar_t *wpattern;
WIN32_FIND_DATAW fd;
HANDLE h;
if (pattern == NULL)
return 0;
wpattern = utf8_to_wide(pattern);
free(pattern);
if (wpattern == NULL)
return 0;
h = FindFirstFileW(wpattern, &fd);
free(wpattern);
if (h == INVALID_HANDLE_VALUE)
return 0;
do {
char *name = wide_to_utf8(fd.cFileName);
char *path;
if (name == NULL || name[0] == '.') {
free(name);
continue;
}
path = join_path(dir, name);
free(name);
if (path == NULL) {
FindClose(h);
return 0;
}
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if (!add_dir(w, path, base_len, on_add, ctx)) {
free(path);
FindClose(h);
return 0;
}
} else if (!add_file(w, path, base_len, on_add, ctx)) {
free(path);
FindClose(h);
return 0;
}
free(path);
} while (FindNextFileW(h, &fd));
FindClose(h);
return 1;
}
#else
static int add_dir(struct fpk_writer *w, const char *dir, size_t base_len,
fpk_add_dir_cb on_add, void *ctx)
{
DIR *d;
struct dirent *ent;
d = opendir(dir);
if (d == NULL)
return 0;
while ((ent = readdir(d)) != NULL) {
char *path;
struct stat st;
if (ent->d_name[0] == '.')
continue;
path = join_path(dir, ent->d_name);
if (path == NULL) {
closedir(d);
return 0;
}
if (stat(path, &st) != 0) {
free(path);
continue;
}
if (S_ISDIR(st.st_mode)) {
if (!add_dir(w, path, base_len, on_add, ctx)) {
free(path);
closedir(d);
return 0;
}
} else if (S_ISREG(st.st_mode)) {
if (!add_file(w, path, base_len, on_add, ctx)) {
free(path);
closedir(d);
return 0;
}
}
free(path);
}
closedir(d);
return 1;
}
#endif
int fpk_writer_add_dir(struct fpk_writer *w, const char *dir_path,
fpk_add_dir_cb on_add, void *ctx)
{
size_t base_len = strlen(dir_path);
while (base_len > 0 && IS_PATH_SEP(dir_path[base_len - 1]))
base_len--;
return add_dir(w, dir_path, base_len, on_add, ctx) ? 0 : -1;
}
+58
View File
@@ -3,6 +3,17 @@
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <direct.h>
#define MKDIR(path) _mkdir(path)
#define RMDIR(path) _rmdir(path)
#else
#include <sys/stat.h>
#include <unistd.h>
#define MKDIR(path) mkdir(path, 0755)
#define RMDIR(path) rmdir(path)
#endif
#define CHECK(cond, msg) \
do { \
if (!(cond)) { \
@@ -13,6 +24,14 @@
static const char *PAK_PATH = "roundtrip.fpk";
static void write_file(const char *path, const char *data)
{
FILE *fp = fopen(path, "wb");
fwrite(data, 1, strlen(data), fp);
fclose(fp);
}
int main(void)
{
struct fpk_writer *w;
@@ -66,6 +85,45 @@ int main(void)
fpk_close(r);
remove(PAK_PATH);
CHECK(MKDIR("adddir_src") == 0, "mkdir(adddir_src) failed");
CHECK(MKDIR("adddir_src/nested") == 0, "mkdir(adddir_src/nested) failed");
write_file("adddir_src/root.txt", "root");
write_file("adddir_src/nested/leaf.txt", "leaf");
w = fpk_writer_open("adddir.fpk");
CHECK(w != NULL, "fpk_writer_open(adddir) failed");
CHECK(fpk_writer_add_dir(w, "adddir_src", NULL, NULL) == 0,
"fpk_writer_add_dir failed");
CHECK(fpk_writer_finalize(w) == 0, "fpk_writer_finalize(adddir) failed");
fpk_writer_close(w);
r = fpk_open("adddir.fpk");
CHECK(r != NULL, "fpk_open(adddir) failed");
CHECK(fpk_entry_count(r) == 2, "adddir entry_count mismatch");
entry = fpk_find(r, "root.txt");
CHECK(entry != NULL, "fpk_find(root.txt) failed");
data = fpk_read(r, entry, &out_size);
CHECK(data != NULL, "fpk_read(root.txt) failed");
CHECK(out_size == 4 && memcmp(data, "root", 4) == 0,
"root.txt content mismatch");
free(data);
entry = fpk_find(r, "nested/leaf.txt");
CHECK(entry != NULL, "fpk_find(nested/leaf.txt) failed");
data = fpk_read(r, entry, &out_size);
CHECK(data != NULL, "fpk_read(nested/leaf.txt) failed");
CHECK(out_size == 4 && memcmp(data, "leaf", 4) == 0,
"nested/leaf.txt content mismatch");
free(data);
fpk_close(r);
remove("adddir.fpk");
remove("adddir_src/root.txt");
remove("adddir_src/nested/leaf.txt");
RMDIR("adddir_src/nested");
RMDIR("adddir_src");
printf("PASS\n");
return EXIT_SUCCESS;
}
+6 -242
View File
@@ -1,58 +1,20 @@
#if !defined(_WIN32)
#define _FILE_OFFSET_BITS 64
#define _POSIX_C_SOURCE 200809L
#endif
#include "fpk.h"
#include <dirent.h>
#include <getopt.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#ifdef _WIN32
#include <windows.h>
#include <wchar.h>
#define PROGRAM_NAME "fpack.exe"
#define fpack_fseek64(f, off, whence) _fseeki64((f), (off), (whence))
#define fpack_ftell64(f) _ftelli64((f))
#else
#include <time.h>
#define PROGRAM_NAME "fpack"
#define fpack_fseek64(f, off, whence) fseeko((f), (off_t)(off), (whence))
#define fpack_ftell64(f) ftello((f))
#endif
#ifdef _WIN32
static wchar_t *utf8_to_wide(const char *s)
{
int wlen = MultiByteToWideChar(CP_UTF8, 0, s, -1, NULL, 0);
wchar_t *w;
if (wlen <= 0)
return NULL;
w = malloc((size_t)wlen * sizeof(wchar_t));
if (!w)
return NULL;
MultiByteToWideChar(CP_UTF8, 0, s, -1, w, wlen);
return w;
}
static char *wide_to_utf8(const wchar_t *w)
{
int len = WideCharToMultiByte(CP_UTF8, 0, w, -1, NULL, 0, NULL, NULL);
char *s;
if (len <= 0)
return NULL;
s = malloc((size_t)len);
if (!s)
return NULL;
WideCharToMultiByte(CP_UTF8, 0, w, -1, s, len, NULL, NULL);
return s;
}
#endif
static double now_seconds(void)
@@ -127,19 +89,6 @@ static char *dup_str(const char *s)
return out;
}
static char *join_path(const char *dir, const char *name)
{
size_t dir_len = strlen(dir);
size_t name_len = strlen(name);
char *out = malloc(dir_len + 1 + name_len + 1);
memcpy(out, dir, dir_len);
out[dir_len] = '/';
memcpy(out + dir_len + 1, name, name_len);
out[dir_len + 1 + name_len] = '\0';
return out;
}
#ifdef _WIN32
#define IS_PATH_SEP(c) ((c) == '/' || (c) == '\\')
#else
@@ -177,94 +126,15 @@ static char *derive_output_name(const char *input_dir)
return out;
}
static void normalize_path(char *s)
static int on_add(const char *path, uint64_t size, void *ctx)
{
for (; *s; s++) {
if (*s == '\\')
*s = '/';
}
}
static void *read_file(const char *path, size_t *out_size)
{
FILE *fp;
int64_t len;
void *buf;
#ifdef _WIN32
wchar_t *wpath = utf8_to_wide(path);
if (!wpath)
return NULL;
fp = _wfopen(wpath, L"rb");
free(wpath);
#else
fp = fopen(path, "rb");
#endif
if (!fp)
return NULL;
if (fpack_fseek64(fp, 0, SEEK_END) != 0) {
fclose(fp);
return NULL;
}
len = fpack_ftell64(fp);
if (len < 0 || fpack_fseek64(fp, 0, SEEK_SET) != 0) {
fclose(fp);
return NULL;
}
buf = malloc((size_t)len);
if (!buf) {
fclose(fp);
return NULL;
}
if (fread(buf, 1, (size_t)len, fp) != (size_t)len) {
free(buf);
fclose(fp);
return NULL;
}
fclose(fp);
*out_size = (size_t)len;
return buf;
}
static int add_file(struct fpk_writer *w, const char *path, size_t base_len)
{
const char *rel_path = path + base_len;
void *data;
size_t size;
char *norm;
if (interrupted)
return 0;
while (*rel_path == '/')
rel_path++;
norm = dup_str(rel_path);
normalize_path(norm);
data = read_file(path, &size);
if (!data) {
fprintf(stderr, "fpack: cannot read: %s\n", path);
free(norm);
return 0;
}
if (fpk_writer_add(w, norm, data, size) != 0) {
fprintf(stderr, "fpack: cannot add: %s\n", norm);
free(data);
free(norm);
return 0;
}
(void)ctx;
entry_count++;
total_bytes += size;
if (flags & FPACK_VERBOSE)
printf(" %s (%lu bytes)\n", norm, (unsigned long)size);
printf(" %s (%lu bytes)\n", path, (unsigned long)size);
if (flags & FPACK_PROGRESS) {
printf("\rpacked %lu files (%lu bytes)",
@@ -272,111 +142,9 @@ static int add_file(struct fpk_writer *w, const char *path, size_t base_len)
fflush(stdout);
}
free(data);
free(norm);
return 1;
return !interrupted;
}
#ifdef _WIN32
static int add_dir(struct fpk_writer *w, const char *dir, size_t base_len)
{
char *pattern = join_path(dir, "*");
wchar_t *wpattern = utf8_to_wide(pattern);
WIN32_FIND_DATAW fd;
HANDLE h;
free(pattern);
if (!wpattern) {
fprintf(stderr, "fpack: cannot open directory: %s\n", dir);
return 0;
}
h = FindFirstFileW(wpattern, &fd);
free(wpattern);
if (h == INVALID_HANDLE_VALUE) {
fprintf(stderr, "fpack: cannot open directory: %s\n", dir);
return 0;
}
do {
char *name = wide_to_utf8(fd.cFileName);
char *path;
if (!name || name[0] == '.') {
free(name);
continue;
}
path = join_path(dir, name);
free(name);
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if (!add_dir(w, path, base_len)) {
free(path);
FindClose(h);
return 0;
}
} else if (!add_file(w, path, base_len)) {
free(path);
FindClose(h);
return 0;
}
free(path);
} while (FindNextFileW(h, &fd));
FindClose(h);
return 1;
}
#else
static int add_dir(struct fpk_writer *w, const char *dir, size_t base_len)
{
DIR *d;
struct dirent *ent;
d = opendir(dir);
if (!d) {
fprintf(stderr, "fpack: cannot open directory: %s\n", dir);
return 0;
}
while ((ent = readdir(d)) != NULL) {
char *path;
struct stat st;
if (ent->d_name[0] == '.')
continue;
path = join_path(dir, ent->d_name);
if (stat(path, &st) != 0) {
fprintf(stderr, "fpack: cannot stat: %s\n", path);
free(path);
continue;
}
if (S_ISDIR(st.st_mode)) {
if (!add_dir(w, path, base_len)) {
free(path);
closedir(d);
return 0;
}
} else if (S_ISREG(st.st_mode)) {
if (!add_file(w, path, base_len)) {
free(path);
closedir(d);
return 0;
}
}
free(path);
}
closedir(d);
return 1;
}
#endif
static void usage(void)
{
fprintf(stderr, "usage: %s [-o output.fpk] [-p] [-q] [-v] input_dir\n", PROGRAM_NAME);
@@ -404,7 +172,6 @@ int main(int argc, char **argv)
const char *output_arg = NULL;
const char *input_dir;
char *output;
size_t base_len;
struct fpk_writer *w;
int c;
double start_time;
@@ -439,10 +206,6 @@ int main(int argc, char **argv)
start_time = now_seconds();
base_len = strlen(input_dir);
while (base_len > 0 && IS_PATH_SEP(input_dir[base_len - 1]))
base_len--;
w = fpk_writer_open(output);
if (!w) {
fprintf(stderr, "fpack: cannot create: %s\n", output);
@@ -452,7 +215,7 @@ int main(int argc, char **argv)
signal(SIGINT, handle_sigint);
if (!add_dir(w, input_dir, base_len)) {
if (fpk_writer_add_dir(w, input_dir, on_add, NULL) != 0) {
fpk_writer_close(w);
if (interrupted) {
remove(output);
@@ -460,6 +223,7 @@ int main(int argc, char **argv)
free(output);
return 130;
}
fprintf(stderr, "fpack: failed while packing: %s\n", input_dir);
free(output);
return 1;
}