X-Git-Url: https://git.draconx.ca/gitweb/upkg.git/blobdiff_plain/3896f18892f87f3c354cb47965c4064d3ff26f5d..d323b189345af7ff63c37f7501b70d0674cb74a4:/src/package.c diff --git a/src/package.c b/src/package.c index 7b3b723..bd97135 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 @@ -26,6 +25,7 @@ #include #include +#include "upkg.h" #define U_PKG_GET_PRIV(o) \ G_TYPE_INSTANCE_GET_PRIVATE(o, U_PKG_TYPE, struct upkg_priv) @@ -36,6 +36,13 @@ struct upkg_priv { 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,22 +82,72 @@ 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 class member + * will be filled in. + */ +static int find_package_by_name(const char *filename, void *data) +{ + GTypeModule *m = G_TYPE_MODULE(data); + UPkg *upkg = U_PKG(data); + + 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]); + upkg->pkg = upkg_fopen(buf); + if (!upkg->pkg) { + free(buf); + return 1; + } + } + + free(buf); + return 0; +} + static gboolean u_pkg_load(GTypeModule *m) { struct upkg_priv *priv = U_PKG_GET_PRIV(m); int (*init_func)(GTypeModule *); - priv->native = lt_dlopenext(m->name); - if (!priv->native) { - dl_print_errors(m->name); - return FALSE; + if (m->name) { + priv->native = lt_dlopenext(m->name); + if (!priv->native) { + dl_print_errors(m->name); + return FALSE; + } + + init_func = lt_dlsym(priv->native, "init"); + if (!init_func || init_func(m) != 0) { + dl_print_errors(__func__); + lt_dlclose(priv->native); + return FALSE; + } } - init_func = lt_dlsym(priv->native, "init"); - if (!init_func || init_func(m) != 0) { - dl_print_errors(__func__); - lt_dlclose(priv->native); - return FALSE; + if (!U_PKG(m)->pkg) { + lt_dlforeachfile(u_pkg_get_search_path(), find_package_by_name, m); } return TRUE; @@ -81,6 +157,7 @@ static void u_pkg_unload(GTypeModule *m) { struct upkg_priv *priv = U_PKG_GET_PRIV(m); void (*exit_func)(GTypeModule *); + UPkg *upkg = U_PKG(m); if (priv->native) { exit_func = lt_dlsym(priv->native, "exit"); @@ -92,6 +169,11 @@ static void u_pkg_unload(GTypeModule *m) dl_print_errors(__func__); } } + + if (upkg->pkg) { + upkg_close(upkg->pkg); + upkg->pkg = NULL; + } } static void u_pkg_init(UPkg *pkg) @@ -99,21 +181,90 @@ static void u_pkg_init(UPkg *pkg) } -static void u_pkg_class_init(UPkgClass *class) +static void u_pkg_finalize(GObject *o) { - g_type_class_add_private(class, sizeof (struct upkg_priv)); + UPkg *upkg = U_PKG(o); + + if (upkg->pkg) { + upkg_close(upkg->pkg); + } +} +static void u_pkg_class_init(UPkgClass *class) +{ GTypeModuleClass *modclass = G_TYPE_MODULE_CLASS(class); + GObjectClass *objclass = G_OBJECT_CLASS(class); + + const char *modpath = getenv("UOBJECT_MODULE_PATH"); + + g_type_class_add_private(class, sizeof (struct upkg_priv)); if (lt_dlinit() != 0) { dl_print_errors(__func__); } - modclass->load = u_pkg_load; - modclass->unload = u_pkg_unload; + if (modpath && lt_dlsetsearchpath(modpath) != 0) { + dl_print_errors(__func__); + } + if (lt_dladdsearchdir(PKGLIBDIR) != 0) { + dl_print_errors(__func__); + } + + modclass->load = u_pkg_load; + modclass->unload = u_pkg_unload; + objclass->finalize = u_pkg_finalize; +} + +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; } -GTypeModule *u_pkg_new(const char *name) +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_by_name(const char *name) { g_return_val_if_fail(name != NULL, NULL); @@ -131,3 +282,21 @@ GTypeModule *u_pkg_new(const char *name) mod->name = str_cpy_lower(pkgname, name); return mod; } + +GTypeModule *u_pkg_new_by_file(const char *filename) +{ + struct upkg *pkg = upkg_fopen(filename); + if (!pkg) { + return NULL; + } + + GTypeModule *mod = g_object_new(U_PKG_TYPE, NULL); + if (!mod) { + upkg_close(pkg); + return NULL; + } + + mod->name = NULL; + U_PKG(mod)->pkg = pkg; + return mod; +}