]> git.draconx.ca Git - upkg.git/blob - src/uobject/package.c
package: Make the VFS directly open the package file.
[upkg.git] / src / uobject / package.c
1 /*
2  *  upkg: tool for manipulating Unreal Tournament packages.
3  *  Copyright (C) 2009 Nick Bowler
4  *
5  *  This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <ctype.h>
23
24 #include <glib-object.h>
25 #include <ltdl.h>
26
27 #include <uobject/package.h>
28 #include <uobject/vfs.h>
29 #include "upkg.h"
30
31 #define U_PKG_GET_PRIV(o) \
32         G_TYPE_INSTANCE_GET_PRIVATE(o, U_PKG_TYPE, struct upkg_priv)
33
34 struct upkg_priv {
35         lt_dlhandle native;
36 };
37
38 G_DEFINE_TYPE(UPkg, u_pkg, G_TYPE_TYPE_MODULE);
39
40 /* Package search path. */
41 static char  *search_path;
42 static size_t search_path_sz;
43
44 /* List of package file extensions, in descending order of precedence. */
45 static const char u_pkg_exts[][5] = { ".u", ".utx", ".umx", ".uax", ".unr" };
46
47 static char *str_cpy_lower(char *dst, const char *src)
48 {
49         size_t i;
50
51         for (i = 0; src[i]; i++)
52                 dst[i] = tolower(src[i]);
53         dst[i] = 0;
54
55         return dst;
56 }
57
58 static int str_cmp_lower(const char *s1, const char *s2)
59 {
60         size_t i;
61
62         for (i = 0; s1[i] && s2[i]; i++) {
63                 int c1 = tolower(s1[i]), c2 = tolower(s2[i]);
64                 if (c1 < c2)
65                         return -1;
66                 if (c1 > c2)
67                         return 1;
68         }
69
70         if (s1[i])
71                 return 1;
72         if (s2[i])
73                 return -1;
74         return 0;
75 }
76
77 static void dl_print_errors(const char *prefix)
78 {
79         const char *err;
80         while ((err = lt_dlerror())) {
81                 if (prefix) fprintf(stderr, "%s: ", prefix);
82                 fprintf(stderr, "%s\n", err);
83         }
84 }
85
86 static gboolean u_pkg_load(GTypeModule *m)
87 {
88         struct upkg_priv *priv = U_PKG_GET_PRIV(m);
89         int (*init_func)(GTypeModule *);
90
91         /* Ignore failure here until we get rid of native-only packages. */
92         U_PKG(m)->pkg = u_pkg_vfs_open_by_name(m->name);
93
94         priv->native = lt_dlopenext(m->name);
95         if (priv->native) {
96                 init_func = lt_dlsym(priv->native, "init");
97                 if (!init_func || init_func(m) != 0) {
98                         dl_print_errors(__func__);
99                         lt_dlclose(priv->native);
100                         upkg_close(U_PKG(m)->pkg);
101                         return FALSE;
102                 }
103         }
104
105         return TRUE;
106 }
107
108 static void u_pkg_unload(GTypeModule *m)
109 {
110         struct upkg_priv *priv = U_PKG_GET_PRIV(m);
111         void (*exit_func)(GTypeModule *);
112         UPkg *upkg = U_PKG(m);
113
114         if (priv->native) {
115                 exit_func = lt_dlsym(priv->native, "exit");
116                 if (exit_func) {
117                         exit_func(m);
118                 }
119
120                 if (lt_dlclose(priv->native) != 0) {
121                         dl_print_errors(__func__);
122                 }
123
124                 priv->native = NULL;
125         }
126
127         if (upkg->pkg) {
128                 upkg_close(upkg->pkg);
129                 upkg->pkg = NULL;
130         }
131 }
132
133 static void u_pkg_init(UPkg *pkg)
134 {
135
136 }
137
138 static void u_pkg_finalize(GObject *o)
139 {
140         u_pkg_unload(G_TYPE_MODULE(o));
141 }
142
143 static void u_pkg_class_init(UPkgClass *class)
144 {
145         GTypeModuleClass *modclass = G_TYPE_MODULE_CLASS(class);
146         GObjectClass *objclass = G_OBJECT_CLASS(class);
147
148         const char *modpath = getenv("UOBJECT_MODULE_PATH");
149
150         g_type_class_add_private(class, sizeof (struct upkg_priv));
151
152         if (lt_dlinit() != 0) {
153                 dl_print_errors(__func__);
154         }
155
156         if (modpath && lt_dlsetsearchpath(modpath) != 0) {
157                 dl_print_errors(__func__);
158         }
159         if (lt_dladdsearchdir(PKGLIBDIR) != 0) {
160                 dl_print_errors(__func__);
161         }
162
163         modclass->load     = u_pkg_load;
164         modclass->unload   = u_pkg_unload;
165         objclass->finalize = u_pkg_finalize;
166 }
167
168 static int expand_search_path(size_t need)
169 {
170         size_t want = search_path_sz;
171         if (want == 0) want = 1;
172
173         while (want < need)
174                 want *= 2;
175
176         if (want > search_path_sz) {
177                 char *new = realloc(search_path, want);
178                 if (!new) {
179                         return -1;
180                 }
181
182                 search_path    = new;
183                 search_path_sz = want;
184         }
185
186         return 0;
187 }
188
189 const char *u_pkg_get_search_path(void)
190 {
191         return search_path ? search_path : "";
192 }
193
194 int u_pkg_set_search_path(const char *path)
195 {
196         if (expand_search_path(strlen(path)+1) != 0)
197                 return -1;
198         strcpy(search_path, path);
199         return 0;
200 }
201
202 int u_pkg_add_search_dir(const char *path)
203 {
204         size_t end = search_path ? strlen(search_path) : 0;
205
206         if (end == 0) {
207                 return u_pkg_set_search_path(path);
208         }
209
210         if (expand_search_path(end + strlen(path) + 2) != 0)
211                 return -1;
212         search_path[end] = LT_PATHSEP_CHAR;
213         strcpy(search_path+end+1, path);
214         return 0;
215 }
216
217 GTypeModule *u_pkg_open(const char *name)
218 {
219         g_return_val_if_fail(name != NULL, NULL);
220
221         char *pkgname = malloc(strlen(name)+1);
222         if (!pkgname) {
223                 return NULL;
224         }
225
226         GTypeModule *mod = g_object_new(U_PKG_TYPE, NULL);
227         if (!mod) {
228                 free(pkgname);
229                 return NULL;
230         }
231
232         mod->name = str_cpy_lower(pkgname, name);
233         return mod;
234 }