Coverage Report

Created: 2026-08-14 07:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vlc/contrib/contrib-build/libmodplug/src/load_umx.cpp
Line
Count
Source
1
/*
2
 * This source code is public domain.
3
 *
4
 * Epic Games Unreal UMX container loading for libmodplug
5
 * Written by O. Sezer <sezero@users.sourceforge.net>
6
 * UPKG parsing partially based on Unreal Media Ripper (UMR) v0.3
7
 * by Andy Ward <wardwh@swbell.net>, with additional updates
8
 * by O. Sezer - see git repo at https://github.com/sezero/umr/
9
 * Retrieves the offset, size and object type directly from umx.
10
*/
11
12
#include "stdafx.h"
13
#include "sndfile.h"
14
15
16
typedef LONG fci_t;   /* FCompactIndex */
17
18
549
#define UPKG_HDR_TAG  0x9e2a83c1
19
20
struct _genhist { /* for upkg versions >= 68 */
21
  LONG export_count;
22
  LONG name_count;
23
};
24
25
struct upkg_hdr {
26
  DWORD tag;  /* UPKG_HDR_TAG */
27
  LONG file_version;
28
  DWORD pkg_flags;
29
  LONG name_count;  /* number of names in name table (>= 0) */
30
  LONG name_offset;   /* offset to name table  (>= 0) */
31
  LONG export_count;  /* num. exports in export table  (>= 0) */
32
  LONG export_offset;   /* offset to export table (>= 0) */
33
  LONG import_count;  /* num. imports in export table  (>= 0) */
34
  LONG import_offset;   /* offset to import table (>= 0) */
35
36
  /* number of GUIDs in heritage table (>= 1) and table's offset:
37
   * only with versions < 68. */
38
  LONG heritage_count;
39
  LONG heritage_offset;
40
  /* with versions >= 68:  a GUID, a dword for generation count
41
   * and export_count and name_count dwords for each generation: */
42
  DWORD guid[4];
43
  LONG generation_count;
44
9.88k
#define UPKG_HDR_SIZE 64      /* 64 bytes up until here */
45
  /*struct _genhist *gen;*/
46
};
47
48
0
#define UMUSIC_IT 0
49
0
#define UMUSIC_S3M  1
50
0
#define UMUSIC_XM 2
51
0
#define UMUSIC_MOD  3
52
#define UMUSIC_WAV  4
53
#define UMUSIC_MP2  5
54
55
static const char *mustype[] = {
56
  "IT", "S3M", "XM", "MOD",
57
  NULL
58
};
59
60
/* decode an FCompactIndex.
61
 * original documentation by Tim Sweeney was at
62
 * http://unreal.epicgames.com/Packages.htm
63
 * also see Unreal Wiki:
64
 * http://wiki.beyondunreal.com/Legacy:Package_File_Format/Data_Details
65
 */
66
static fci_t get_fci (const char *in, int *pos)
67
0
{
68
0
  LONG a;
69
0
  int size;
70
71
0
  size = 1;
72
0
  a = in[0] & 0x3f;
73
74
0
  if (in[0] & 0x40) {
75
0
    size++;
76
0
    a |= (in[1] & 0x7f) << 6;
77
78
0
    if (in[1] & 0x80) {
79
0
      size++;
80
0
      a |= (in[2] & 0x7f) << 13;
81
82
0
      if (in[2] & 0x80) {
83
0
        size++;
84
0
        a |= (in[3] & 0x7f) << 20;
85
86
0
        if (in[3] & 0x80) {
87
0
          size++;
88
0
          a |= (in[4] & 0x3f) << 27;
89
0
        }
90
0
      }
91
0
    }
92
0
  }
93
94
0
  if (in[0] & 0x80)
95
0
    a = -a;
96
97
0
  *pos += size;
98
99
0
  return a;
100
0
}
101
102
static int get_objtype (const BYTE *membase, LONG memlen,
103
      LONG ofs, int type)
104
0
{
105
0
  if (type == UMUSIC_IT) {
106
0
  _retry:
107
0
    if (memcmp(membase + ofs, "IMPM", 4) == 0)
108
0
      return UMUSIC_IT;
109
0
    return -1;
110
0
  }
111
0
  if (type == UMUSIC_XM) {
112
0
    if (memcmp(membase + ofs, "Extended Module: ", 17) != 0)
113
0
      return -1;
114
0
    if (*(membase + ofs + 37) != 0x1a) return -1;
115
0
    return UMUSIC_XM;
116
0
  }
117
118
0
  if (type == UMUSIC_S3M) {
119
0
    if (memcmp(membase + ofs + 44, "SCRM", 4) == 0)
120
0
      return UMUSIC_S3M;
121
    /*return -1;*/
122
    /* SpaceMarines.umx and Starseek.umx from Return to NaPali
123
     * report as "s3m" whereas the actual music format is "it" */
124
0
    goto _retry;
125
0
  }
126
127
0
  if (type == UMUSIC_MOD) {
128
0
    membase += ofs + 1080;
129
0
    if (memcmp(membase, "M.K.", 4) == 0 || memcmp(membase, "M!K!", 4) == 0)
130
0
      return UMUSIC_MOD;
131
0
    return -1;
132
0
  }
133
134
0
  return -1;
135
0
}
136
137
static int read_export (const BYTE *membase, LONG memlen,
138
      const struct upkg_hdr *hdr,
139
      LONG *ofs, LONG *objsize)
140
0
{
141
0
  char buf[40];
142
0
  int idx = 0, t;
143
144
0
  memcpy(buf, membase + *ofs, 40);
145
146
0
  if (hdr->file_version < 40) idx += 8; /* 00 00 00 00 00 00 00 00 */
147
0
  if (hdr->file_version < 60) idx += 16; /* 81 00 00 00 00 00 FF FF FF FF FF FF FF FF 00 00 */
148
0
  get_fci(&buf[idx], &idx);   /* skip junk */
149
0
  t = get_fci(&buf[idx], &idx);   /* type_name */
150
0
  if (hdr->file_version > 61) idx += 4; /* skip export size */
151
0
  *objsize = get_fci(&buf[idx], &idx);
152
0
  *ofs += idx;  /* offset for real data */
153
154
0
  return t; /* return type_name index */
155
0
}
156
157
static int read_typname(const BYTE *membase, LONG memlen,
158
      const struct upkg_hdr *hdr,
159
      int idx, char *out)
160
0
{
161
0
  int i, s;
162
0
  long l;
163
0
  char buf[64];
164
165
0
  if (idx >= hdr->name_count) return -1;
166
0
  buf[63] = '\0';
167
0
  for (i = 0, l = 0; i <= idx; i++) {
168
0
    memcpy(buf, membase + hdr->name_offset + l, 63);
169
0
    if (hdr->file_version >= 64) {
170
0
      s = *(signed char *)buf; /* numchars *including* terminator */
171
0
      if (s <= 0 || s > 64) return -1;
172
0
      l += s + 5; /* 1 for buf[0], 4 for int32_t name_flags */
173
0
    } else {
174
0
      l += (long)strlen(buf);
175
0
      l +=  5;  /* 1 for terminator, 4 for int32_t name_flags */
176
0
    }
177
0
  }
178
179
0
  strcpy(out, (hdr->file_version >= 64)? &buf[1] : buf);
180
0
  return 0;
181
0
}
182
183
static int probe_umx   (const BYTE *membase, LONG memlen,
184
      const struct upkg_hdr *hdr,
185
      LONG *ofs, LONG *objsize)
186
0
{
187
0
  int i, idx, t;
188
0
  LONG s, pos;
189
0
  char buf[64];
190
191
  /* Find the offset and size of the first IT, S3M or XM
192
   * by parsing the exports table. The umx files should
193
   * have only one export. Kran32.umx from Unreal has two,
194
   * but both pointing to the same music. */
195
0
  s = memlen - hdr->export_offset;
196
0
  if (s <= 0) return -1;
197
0
  if (s > 64) s = 64;
198
0
  memcpy(buf, membase + hdr->export_offset, s);
199
0
  for (; s < 64; ++s) buf[s] = 0x0; /* really? */
200
201
0
  idx = 0;
202
203
0
  get_fci(&buf[idx], &idx); /* skip class_index */
204
0
  get_fci(&buf[idx], &idx); /* skip super_index */
205
0
  if (hdr->file_version >= 60) idx += 4; /* skip int32 package_index */
206
0
  get_fci(&buf[idx], &idx); /* skip object_name */
207
0
  idx += 4;     /* skip int32 object_flags */
208
209
0
  s = get_fci(&buf[idx], &idx); /* get serial_size */
210
0
  if (s <= 0) return -1;
211
0
  pos = get_fci(&buf[idx],&idx);  /* get serial_offset */
212
0
  if (pos < 0 || pos > memlen - 40) return -1;
213
214
0
  if ((t = read_export(membase, memlen, hdr, &pos, &s)) < 0) return -1;
215
0
  if (s <= 0 || s > memlen - pos) return -1;
216
217
0
  if (read_typname(membase, memlen, hdr, t, buf) < 0) return -1;
218
0
  for (i = 0; mustype[i] != NULL; i++) {
219
0
    if (!strcasecmp(buf, mustype[i])) {
220
0
      t = i;
221
0
      break;
222
0
    }
223
0
  }
224
0
  if (mustype[i] == NULL) return -1;
225
0
  if ((t = get_objtype(membase, memlen, pos, t)) < 0) return -1;
226
227
0
  *ofs = pos;
228
0
  *objsize = s;
229
0
  return t;
230
0
}
231
232
static int probe_header (void *header)
233
549
{
234
549
  struct upkg_hdr *hdr;
235
549
  unsigned char *p;
236
549
  DWORD *swp;
237
549
  int i;
238
239
  /* byte swap the header - all members are 32 bit LE values */
240
549
  p = (unsigned char *) header;
241
549
  swp = (DWORD *) header;
242
9.33k
  for (i = 0; i < UPKG_HDR_SIZE/4; i++, p += 4) {
243
8.78k
    swp[i] = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24);
244
8.78k
  }
245
246
549
  hdr = (struct upkg_hdr *) header;
247
549
  if (hdr->tag != UPKG_HDR_TAG) {
248
549
    return -1;
249
549
  }
250
0
  if (hdr->name_count < 0  ||
251
0
      hdr->name_offset  < 0  ||
252
0
      hdr->export_count < 0  ||
253
0
      hdr->export_offset  < 0  ||
254
0
      hdr->import_count < 0  ||
255
0
      hdr->import_offset  < 0  ) {
256
0
    return -1;
257
0
  }
258
259
#if 0
260
  return 0;
261
#else
262
0
  switch (hdr->file_version) {
263
0
  case 35: case 37:  /* Unreal beta - */
264
0
  case 40: case 41:        /* 1998 */
265
0
  case 61:/* Unreal */
266
0
  case 62:/* Unreal Tournament */
267
0
  case 63:/* Return to NaPali */
268
0
  case 64:/* Unreal Tournament */
269
0
  case 66:/* Unreal Tournament */
270
0
  case 68:/* Unreal Tournament */
271
0
  case 69:/* Tactical Ops */
272
0
  case 83:/* Mobile Forces */
273
0
    return 0;
274
0
  }
275
276
0
  return -1;/* Unknown upkg version for an UMX */
277
0
#endif
278
0
}
279
280
static int process_upkg (const BYTE *membase, LONG memlen,
281
       LONG *ofs, LONG *objsize)
282
549
{
283
549
  char header[UPKG_HDR_SIZE];
284
285
549
  memcpy(header, membase, UPKG_HDR_SIZE);
286
549
  if (probe_header(header) < 0)
287
549
    return -1;
288
289
0
  return probe_umx(membase, memlen, (struct upkg_hdr *)header, ofs, objsize);
290
549
}
291
292
BOOL CSoundFile::ReadUMX(const BYTE *lpStream, DWORD dwMemLength)
293
//---------------------------------------------------------------
294
1.56k
{
295
1.56k
  int type;
296
1.56k
  LONG ofs = 0, size = 0;
297
298
1.56k
  if (!lpStream || dwMemLength < 0x800 || dwMemLength > 0x7fffffff)
299
1.01k
    return FALSE;
300
549
  type = process_upkg(lpStream, (LONG)dwMemLength, &ofs, &size);
301
549
  if (type < 0) return FALSE;
302
303
  // Rip Mods from UMX
304
0
  switch (type) {
305
0
  case UMUSIC_IT:  return ReadIT(lpStream + ofs, size);
306
0
  case UMUSIC_S3M: return ReadS3M(lpStream + ofs, size);
307
0
  case UMUSIC_XM:  return ReadXM(lpStream + ofs, size);
308
0
  case UMUSIC_MOD: return ReadMod(lpStream + ofs, size);
309
0
  }
310
0
  return FALSE;
311
0
}
312