/* * upkg: tool for manipulating Unreal Tournament packages. * Copyright (C) 2009 Nick Bowler * * 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 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 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, see . */ #include #include #include #include #include #include #include #include "upkg.h" #define U_PKG_GET_PRIV(o) \ G_TYPE_INSTANCE_GET_PRIVATE(o, U_PKG_TYPE, struct upkg_priv) struct upkg_priv { lt_dlhandle native; }; 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; for (i = 0; src[i]; i++) dst[i] = tolower(src[i]); dst[i] = 0; 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; while ((err = lt_dlerror())) { if (prefix) fprintf(stderr, "%s: ", prefix); fprintf(stderr, "%s\n", err); } } /* * 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 *); 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; } } if (!U_PKG(m)->pkg) { lt_dlforeachfile(u_pkg_get_search_path(), find_package_by_name, m); } return TRUE; } 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"); if (exit_func) { exit_func(m); } if (lt_dlclose(priv->native) != 0) { dl_print_errors(__func__); } } if (upkg->pkg) { upkg_close(upkg->pkg); upkg->pkg = NULL; } } static void u_pkg_init(UPkg *pkg) { } static void u_pkg_finalize(GObject *o) { 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__); } 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; } 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); char *pkgname = malloc(strlen(name)+1); if (!pkgname) { return NULL; } GTypeModule *mod = g_object_new(U_PKG_TYPE, NULL); if (!mod) { free(pkgname); return NULL; } 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; }