]> git.draconx.ca Git - upkg.git/blob - src/engine/music.c
Implement support for libmodplug in Engine.Music.
[upkg.git] / src / engine / music.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <glib-object.h>
4
5 #include "exportable.h"
6 #include "loadable.h"
7 #include "uobject.h"
8 #include "music-module.h"
9 #include "music.h"
10 #include "upkg.h"
11
12 #define MUSIC_GET_PRIV(o) \
13         G_TYPE_INSTANCE_GET_PRIVATE(o, ENGINE_MUSIC_TYPE, struct music_priv)
14
15 struct music_priv {
16         struct upkg_file *f;
17         struct music_mod *mod;
18         unsigned loaded;
19 };
20
21 static void exportable_init(UObjectExportable *);
22 static void loadable_init(UObjectLoadable *);
23
24 G_DEFINE_DYNAMIC_TYPE_EXTENDED(EngineMusic, engine_music, U_OBJECT_TYPE, 0,
25         G_IMPLEMENT_INTERFACE(U_OBJECT_TYPE_EXPORTABLE, exportable_init)
26         G_IMPLEMENT_INTERFACE(U_OBJECT_TYPE_LOADABLE, loadable_init)
27 );
28
29 static int load(GObject *o)
30 {
31         struct music_priv *priv = MUSIC_GET_PRIV(o);
32
33         if (!priv->loaded) {
34                 g_return_val_if_fail(priv->f != NULL, -1);
35
36                 if (upkg_export_seek(priv->f, 0, SEEK_SET) != 0) {
37                         return -1;
38                 }
39
40                 priv->mod = music_mod_open(priv->f);
41                 if (!priv->mod) {
42                         return -1;
43                 }
44         }
45
46         priv->loaded++;
47         return 0;
48 }
49
50 static void unload(GObject *o)
51 {
52         struct music_priv *priv = MUSIC_GET_PRIV(o);
53         g_return_if_fail(priv->loaded > 0);
54
55         if (--priv->loaded == 0) {
56                 music_mod_close(priv->mod);
57         }
58 }
59
60 static int export(GObject *o, FILE *f)
61 {
62         struct music_priv *priv = MUSIC_GET_PRIV(o);
63         int rc;
64
65         if (load(o) != 0)
66                 return -1;
67
68         rc = music_mod_dump(priv->mod, f);
69
70         unload(o);
71
72         return rc;
73 }
74
75 static int export_name(GObject *o, char *buf, size_t n)
76 {
77         struct music_priv *priv = MUSIC_GET_PRIV(o);
78         const char *type;
79         int rc;
80
81         if (load(o) != 0) {
82                 if (n > 0) *buf = 0;
83                 return 0;
84         }
85
86         type = music_mod_type(priv->mod);
87         rc = snprintf(buf, n, "%s.%s", priv->f->name, type);
88
89         unload(o);
90
91         return rc;
92 }
93
94 static void exportable_init(UObjectExportable *e)
95 {
96         e->export      = export;
97         e->export_name = export_name;
98 }
99
100 static void loadable_init(UObjectLoadable *l)
101 {
102         l->load   = load;
103         l->unload = unload;
104 }
105
106 static int deserialize(UObject *o, struct upkg_file *f)
107 {
108         struct music_priv *priv = MUSIC_GET_PRIV(o);
109         size_t rc, pos = 0, buflen;
110         unsigned char buf[32];
111         long size;
112
113         U_OBJECT_CLASS(engine_music_parent_class)->deserialize(o, f);
114
115         buflen = upkg_export_read(f, buf, sizeof buf);
116
117         /* Random field #1 */
118         if (buflen - pos < 1)
119                 return -1;
120         pos += 1;
121
122         if (f->pkg->version > 61) {
123                 /* Random field #2 */
124                 if (buflen - pos < 4)
125                         return -1;
126                 pos += 4;
127         }
128
129         rc = upkg_decode_index(&size, buf+pos, buflen-pos);
130         if (rc == 0 || size < 0)
131                 return -1;
132         pos += rc;
133
134         f->base += pos;
135         f->len   = size;
136         upkg_export_seek(f, 0, SEEK_SET);
137         priv->f = f;
138
139         return 0;
140 }
141
142 void music_register(GTypeModule *m)
143 {
144         engine_music_register_type(m);
145 }
146
147 static void engine_music_init(EngineMusic *m)
148 {
149         struct music_priv *priv = MUSIC_GET_PRIV(m);
150         *priv = (struct music_priv){0};
151 }
152
153 static void engine_music_finalize(GObject *o)
154 {
155         struct music_priv *priv = MUSIC_GET_PRIV(o);
156
157         if (priv->loaded >= 1) {
158                 priv->loaded = 1;
159                 unload(o);
160         }
161 }
162
163 static void engine_music_class_init(EngineMusicClass *class)
164 {
165         UObjectClass *uo = U_OBJECT_CLASS(class);
166         GObjectClass *go = G_OBJECT_CLASS(class);
167         g_type_class_add_private(class, sizeof (struct music_priv));
168
169         uo->deserialize = deserialize;
170         go->finalize    = engine_music_finalize;
171
172         music_mod_init();
173 }
174
175 static void engine_music_class_finalize(EngineMusicClass *class)
176 {
177         music_mod_exit();
178 }