X-Git-Url: https://git.draconx.ca/gitweb/upkg.git/blobdiff_plain/291e78f75bc8cc49c7191e4d69ad5843dc810ddd..33c1796d255a720ad076160efd6859ca74d0792f:/src/package.c diff --git a/src/package.c b/src/package.c index 1de6096..cbbaf09 100644 --- a/src/package.c +++ b/src/package.c @@ -2,9 +2,9 @@ * upkg: tool for manipulating Unreal Tournament packages. * Copyright (C) 2009 Nick Bowler * - * This program is free software; you can redistribute it and/or modify + * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, @@ -13,8 +13,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * along with this program. If not, see . */ #include @@ -32,10 +31,18 @@ struct upkg_priv { lt_dlhandle native; + FILE *pkg_file; }; G_DEFINE_TYPE(UPkg, u_pkg, G_TYPE_TYPE_MODULE); +/* Package search path. */ +static char *search_path; +static size_t search_path_sz; + +/* List of package file extensions, in descending order of precedence. */ +static const char u_pkg_exts[][5] = { ".u", ".utx", ".umx", ".uax", ".unr" }; + static char *str_cpy_lower(char *dst, const char *src) { size_t i; @@ -47,6 +54,25 @@ static char *str_cpy_lower(char *dst, const char *src) return dst; } +static int str_cmp_lower(const char *s1, const char *s2) +{ + size_t i; + + for (i = 0; s1[i] && s2[i]; i++) { + int c1 = tolower(s1[i]), c2 = tolower(s2[i]); + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + } + + if (s1[i]) + return 1; + if (s2[i]) + return -1; + return 0; +} + static void dl_print_errors(const char *prefix) { const char *err; @@ -56,6 +82,50 @@ static void dl_print_errors(const char *prefix) } } +/* + * Function for use with lt_dlforeachfile. The user data must point to + * a UPkg with the GTypeModule name field initialized. If a suitable file + * is found and could be successfully opened for reading, the pkg_file member + * of struct upkg_priv will be filled in. + */ +static int find_package_by_name(const char *filename, void *_pkg) +{ + struct upkg_priv *priv = U_PKG_GET_PRIV(_pkg); + GTypeModule *m = _pkg; + + const char *base; + size_t len; + char *buf; + + base = strrchr(filename, '/'); + if (base) { + base++; + } else { + base = filename; + } + + if (str_cmp_lower(base, m->name) != 0) + return 0; + + len = strlen(filename); + buf = malloc(len + sizeof **u_pkg_exts); + if (!buf) + return 0; + strcpy(buf, filename); + + for (unsigned i = 0; i < sizeof u_pkg_exts / sizeof *u_pkg_exts; i++) { + strcpy(buf+len, u_pkg_exts[i]); + priv->pkg_file = fopen(buf, "rb"); + if (priv->pkg_file) { + free(buf); + return 1; + } + } + + free(buf); + return 0; +} + static gboolean u_pkg_load(GTypeModule *m) { struct upkg_priv *priv = U_PKG_GET_PRIV(m); @@ -74,6 +144,8 @@ static gboolean u_pkg_load(GTypeModule *m) return FALSE; } + lt_dlforeachfile(u_pkg_get_search_path(), find_package_by_name, m); + return TRUE; } @@ -92,6 +164,10 @@ static void u_pkg_unload(GTypeModule *m) dl_print_errors(__func__); } } + + if (priv->pkg_file) { + fclose(priv->pkg_file); + } } static void u_pkg_init(UPkg *pkg) @@ -101,11 +177,19 @@ static void u_pkg_init(UPkg *pkg) static void u_pkg_class_init(UPkgClass *class) { + GTypeModuleClass *modclass = G_TYPE_MODULE_CLASS(class); + const char *modpath = getenv("UOBJECT_MODULE_PATH"); + g_type_class_add_private(class, sizeof (struct upkg_priv)); - GTypeModuleClass *modclass = G_TYPE_MODULE_CLASS(class); + if (lt_dlinit() != 0) { + dl_print_errors(__func__); + } - if (lt_dlinit() != 0 || lt_dlsetsearchpath(PKGLIBDIR) != 0) { + if (modpath && lt_dlsetsearchpath(modpath) != 0) { + dl_print_errors(__func__); + } + if (lt_dladdsearchdir(PKGLIBDIR) != 0) { dl_print_errors(__func__); } @@ -113,6 +197,55 @@ static void u_pkg_class_init(UPkgClass *class) modclass->unload = u_pkg_unload; } +static int expand_search_path(size_t need) +{ + size_t want = search_path_sz; + if (want == 0) want = 1; + + while (want < need) + want *= 2; + + if (want > search_path_sz) { + char *new = realloc(search_path, want); + if (!new) { + return -1; + } + + search_path = new; + search_path_sz = want; + } + + return 0; +} + +const char *u_pkg_get_search_path(void) +{ + return search_path ? search_path : ""; +} + +int u_pkg_set_search_path(const char *path) +{ + if (expand_search_path(strlen(path)+1) != 0) + return -1; + strcpy(search_path, path); + return 0; +} + +int u_pkg_add_search_dir(const char *path) +{ + size_t end = search_path ? strlen(search_path) : 0; + + if (end == 0) { + return u_pkg_set_search_path(path); + } + + if (expand_search_path(end + strlen(path) + 2) != 0) + return -1; + search_path[end] = LT_PATHSEP_CHAR; + strcpy(search_path+end+1, path); + return 0; +} + GTypeModule *u_pkg_new(const char *name) { g_return_val_if_fail(name != NULL, NULL);