int xmp_load_module(xmp_context opaque, char *path) { struct context_data *ctx = (struct context_data *)opaque; struct module_data *m = &ctx->m; HIO_HANDLE *h; struct stat st; struct list_head tmpfiles_list; D_(D_WARN "path = %s", path); if (stat(path, &st) < 0) return -XMP_ERROR_SYSTEM; #ifndef _MSC_VER if (S_ISDIR(st.st_mode)) { errno = EISDIR; return -XMP_ERROR_SYSTEM; } #endif if ((h = hio_open_file(path, "rb")) == NULL) return -XMP_ERROR_SYSTEM; INIT_LIST_HEAD(&tmpfiles_list); D_(D_INFO "decrunch"); if (decrunch(&tmpfiles_list, &h->f, &path, DECRUNCH_MAX) < 0) goto err_depack; if (hio_stat(h, &st) < 0) goto err_depack; if (st.st_size < 256) { /* get size after decrunch */ hio_close(h); unlink_tempfiles(&tmpfiles_list); return -XMP_ERROR_FORMAT; } if (ctx->state > XMP_STATE_UNLOADED) xmp_release_module(opaque); m->dirname = get_dirname(path); if (m->dirname == NULL) return -XMP_ERROR_SYSTEM; m->basename = get_basename(path); if (m->basename == NULL) return -XMP_ERROR_SYSTEM; m->filename = path; /* For ALM, SSMT, etc */ m->size = st.st_size; return load_module(opaque, h, &tmpfiles_list); err_depack: hio_close(h); unlink_tempfiles(&tmpfiles_list); return -XMP_ERROR_DEPACK; }
static void set_md5sum(HIO_HANDLE *f, unsigned char *digest) { unsigned char buf[BUFLEN]; MD5_CTX ctx; int bytes_read; struct stat st; if (hio_stat(f, &st) < 0) return; if (st.st_size <= 0) { memset(digest, 0, 16); return; } hio_seek(f, 0, SEEK_SET); MD5Init(&ctx); while ((bytes_read = hio_read(buf, 1, BUFLEN, f)) > 0) { MD5Update(&ctx, buf, bytes_read); } MD5Final(digest, &ctx); }
static int alm_load(struct module_data *m, HIO_HANDLE *f, const int start) { struct xmp_module *mod = &m->mod; int i, j; struct alm_file_header afh; struct xmp_event *event; struct stat stat; uint8 b; char *basename; char filename[NAME_SIZE]; char modulename[NAME_SIZE]; LOAD_INIT(); hio_read(&afh.id, 7, 1, f); if (!strncmp((char *)afh.id, "ALEYMOD", 7)) /* Version 1.0 */ mod->spd = afh.speed / 2; strncpy(modulename, m->filename, NAME_SIZE); basename = strtok (modulename, "."); afh.speed = hio_read8(f); afh.length = hio_read8(f); afh.restart = hio_read8(f); hio_read(&afh.order, 128, 1, f); mod->len = afh.length; mod->rst = afh.restart; memcpy (mod->xxo, afh.order, mod->len); for (mod->pat = i = 0; i < mod->len; i++) if (mod->pat < afh.order[i]) mod->pat = afh.order[i]; mod->pat++; mod->ins = 31; mod->trk = mod->pat * mod->chn; mod->smp = mod->ins; m->c4rate = C4_NTSC_RATE; set_type(m, "Aley's Module"); MODULE_INFO(); if (pattern_init(mod) < 0) return -1; /* Read and convert patterns */ D_(D_INFO "Stored patterns: %d", mod->pat); for (i = 0; i < mod->pat; i++) { if (pattern_tracks_alloc(mod, i, 64) < 0) return -1; for (j = 0; j < 64 * mod->chn; j++) { event = &EVENT (i, j % mod->chn, j / mod->chn); b = hio_read8(f); if (b) event->note = (b == 37) ? 0x61 : b + 48; event->ins = hio_read8(f); } } if (instrument_init(mod) < 0) return -1; /* Read and convert instruments and samples */ D_(D_INFO "Loading samples: %d", mod->ins); for (i = 0; i < mod->ins; i++) { HIO_HANDLE *s; if (subinstrument_alloc(mod, i, 1) < 0) return -1; mod->xxi[i].sub = calloc(sizeof (struct xmp_subinstrument), 1); snprintf(filename, NAME_SIZE, "%s.%d", basename, i + 1); s = hio_open(filename, "rb"); if (s == NULL) continue; mod->xxi[i].nsm = 1; hio_stat(s, &stat); b = hio_read8(s); /* Get first octet */ mod->xxs[i].len = stat.st_size - 5 * !b; if (!b) { /* Instrument with header */ mod->xxs[i].lps = hio_read16l(f); mod->xxs[i].lpe = hio_read16l(f); mod->xxs[i].flg = mod->xxs[i].lpe > mod->xxs[i].lps ? XMP_SAMPLE_LOOP : 0; } else { hio_seek(s, 0, SEEK_SET); } mod->xxi[i].sub[0].pan = 0x80; mod->xxi[i].sub[0].vol = 0x40; mod->xxi[i].sub[0].sid = i; D_(D_INFO "[%2X] %-14.14s %04x %04x %04x %c V%02x", i, filename, mod->xxs[i].len, mod->xxs[i].lps, mod->xxs[i].lpe, mod->xxs[i].flg & XMP_SAMPLE_LOOP ? 'L' : ' ', mod->xxi[i].sub[0].vol); if (load_sample(m, s, SAMPLE_FLAG_UNS, &mod->xxs[i], NULL) < 0) return -1; hio_close(s); } /* ALM is LRLR, not LRRL */ for (i = 0; i < mod->chn; i++) mod->xxc[i].pan = DEFPAN((i % 2) * 0xff); return 0; }
int xmp_load_module(xmp_context opaque, char *path) { struct context_data *ctx = (struct context_data *)opaque; struct module_data *m = &ctx->m; HIO_HANDLE *h; struct stat st; struct list_head tmpfiles_list; int test_result, load_result; int i, ret; D_(D_WARN "path = %s", path); if (stat(path, &st) < 0) return -XMP_ERROR_SYSTEM; #ifndef _MSC_VER if (S_ISDIR(st.st_mode)) { errno = EISDIR; return -XMP_ERROR_SYSTEM; } #endif if ((h = hio_open_file(path, "rb")) == NULL) return -XMP_ERROR_SYSTEM; INIT_LIST_HEAD(&tmpfiles_list); D_(D_INFO "decrunch"); if (decrunch(&tmpfiles_list, &h->f, &path, DECRUNCH_MAX) < 0) goto err_depack; if (hio_stat(h, &st) < 0) goto err_depack; if (st.st_size < 256) { /* get size after decrunch */ hio_close(h); unlink_tempfiles(&tmpfiles_list); return -XMP_ERROR_FORMAT; } if (ctx->state > XMP_STATE_UNLOADED) xmp_release_module(opaque); m->dirname = get_dirname(path); if (m->dirname == NULL) return -XMP_ERROR_SYSTEM; m->basename = get_basename(path); if (m->basename == NULL) return -XMP_ERROR_SYSTEM; m->filename = path; /* For ALM, SSMT, etc */ m->size = st.st_size; load_prologue(ctx); D_(D_WARN "load"); test_result = load_result = -1; for (i = 0; format_loader[i] != NULL; i++) { hio_seek(h, 0, SEEK_SET); test_result = format_loader[i]->test(h, NULL, 0); if (test_result == 0) { hio_seek(h, 0, SEEK_SET); D_(D_WARN "load format: %s", format_loader[i]->name); load_result = format_loader[i]->loader(m, h, 0); break; } } set_md5sum(h, m->md5); hio_close(h); unlink_tempfiles(&tmpfiles_list); if (test_result < 0) { free(m->basename); free(m->dirname); return -XMP_ERROR_FORMAT; } if (load_result < 0) { xmp_release_module(opaque); return -XMP_ERROR_LOAD; } str_adj(m->mod.name); if (!*m->mod.name) { strncpy(m->mod.name, m->basename, XMP_NAME_SIZE); } load_epilogue(ctx); ret = prepare_scan(ctx); if (ret < 0) return ret; scan_sequences(ctx); ctx->state = XMP_STATE_LOADED; return 0; err_depack: hio_close(h); unlink_tempfiles(&tmpfiles_list); return -XMP_ERROR_DEPACK; }
static int decrunch(struct list_head *head, FILE **f, char **s, int ttl) { unsigned char b[1024]; char *cmd; FILE *t; int fd, builtin, res; char *temp2, tmp[PATH_MAX]; struct tmpfilename *temp; int headersize; cmd = NULL; builtin = res = 0; if (get_temp_dir(tmp, PATH_MAX) < 0) return 0; strncat(tmp, "xmp_XXXXXX", PATH_MAX); fseek(*f, 0, SEEK_SET); if ((headersize = fread(b, 1, 1024, *f)) < 100) /* minimum valid file size */ return 0; #if defined __AMIGA__ && !defined __AROS__ if (test_xfd(b, 1024)) { builtin = BUILTIN_XFD; } else #endif if (b[0] == 'P' && b[1] == 'K' && ((b[2] == 3 && b[3] == 4) || (b[2] == '0' && b[3] == '0' && b[4] == 'P' && b[5] == 'K' && b[6] == 3 && b[7] == 4))) { /* Zip */ builtin = BUILTIN_ZIP; } else if (b[2] == '-' && b[3] == 'l' && b[4] == 'h') { /* LHa */ builtin = BUILTIN_LHA; } else if (b[0] == 31 && b[1] == 139) { /* gzip */ builtin = BUILTIN_GZIP; } else if (b[0] == 'B' && b[1] == 'Z' && b[2] == 'h') { /* bzip2 */ builtin = BUILTIN_BZIP2; } else if (b[0] == 0xfd && b[3] == 'X' && b[4] == 'Z' && b[5] == 0x00) { /* xz */ builtin = BUILTIN_XZ; #if 0 } else if (b[0] == 'Z' && b[1] == 'O' && b[2] == 'O' && b[3] == ' ') { /* zoo */ builtin = BUILTIN_ZOO; #endif } else if (b[0] == 'M' && b[1] == 'O' && b[2] == '3') { /* MO3 */ cmd = "unmo3 -s \"%s\" STDOUT"; } else if (b[0] == 31 && b[1] == 157) { /* compress */ builtin = BUILTIN_COMPRESS; } else if (memcmp(b, "PP20", 4) == 0) { /* PowerPack */ builtin = BUILTIN_PP; } else if (memcmp(b, "XPKF", 4) == 0 && memcmp(b + 8, "SQSH", 4) == 0) { /* SQSH */ builtin = BUILTIN_SQSH; } else if (!memcmp(b, "Archive\0", 8)) { /* ArcFS */ builtin = BUILTIN_ARCFS; } else if (memcmp(b, "ziRCONia", 8) == 0) { /* MMCMP */ builtin = BUILTIN_MMCMP; } else if (memcmp(b, "MUSE", 4) == 0 && readmem32b(b + 4) == 0xdeadbeaf) { /* J2B MUSE */ builtin = BUILTIN_MUSE; } else if (memcmp(b, "MUSE", 4) == 0 && readmem32b(b + 4) == 0xdeadbabe) { /* MOD2J2B MUSE */ builtin = BUILTIN_MUSE; } else if (memcmp(b, "LZX", 3) == 0) { /* LZX */ builtin = BUILTIN_LZX; } else if (memcmp(b, "Rar", 3) == 0) { /* rar */ cmd = "unrar p -inul -xreadme -x*.diz -x*.nfo -x*.txt " "-x*.exe -x*.com \"%s\""; } else if (memcmp(b, "S404", 4) == 0) { /* Stonecracker */ builtin = BUILTIN_S404; } else if (test_oxm(*f) == 0) { /* oggmod */ builtin = BUILTIN_OXM; } if (builtin == 0 && cmd == NULL && b[0] == 0x1a) { int x = b[1] & 0x7f; int i, flag = 0; long size; /* check file name */ for (i = 0; i < 13; i++) { if (b[2 + i] == 0) { if (i == 0) /* name can't be empty */ flag = 1; break; } if (!isprint(b[2 + i])) { /* name must be printable */ flag = 1; break; } } size = readmem32l(b + 15); /* max file size is 512KB */ if (size < 0 || size > 512 * 1024) flag = 1; if (flag == 0) { if (x >= 1 && x <= 9 && x != 7) { /* Arc */ builtin = BUILTIN_ARC; } else if (x == 0x7f) { /* !Spark */ builtin = BUILTIN_ARC; } } } fseek(*f, 0, SEEK_SET); if (builtin == 0 && cmd == NULL) return 0; #if defined ANDROID || defined __native_client__ /* Don't use external helpers in android */ if (cmd) return 0; #endif D_(D_WARN "Depacking file... "); temp = calloc(sizeof (struct tmpfilename), 1); if (!temp) { D_(D_CRIT "calloc failed"); return -1; } temp->name = strdup(tmp); if (temp->name == NULL || (fd = mkstemp(temp->name)) < 0) { D_(D_CRIT "failed"); return -1; } list_add_tail(&temp->list, head); if ((t = fdopen(fd, "w+b")) == NULL) { D_(D_CRIT "failed"); return -1; } if (cmd) { #define BSIZE 0x4000 int n; char line[1024], buf[BSIZE]; FILE *p; snprintf(line, 1024, cmd, *s); #ifdef WIN32 /* Note: The _popen function returns an invalid file opaque, if * used in a Windows program, that will cause the program to hang * indefinitely. _popen works properly in a Console application. * To create a Windows application that redirects input and output, * read the section "Creating a Child Process with Redirected Input * and Output" in the Win32 SDK. -- Mirko */ if ((p = popen(line, "rb")) == NULL) { #else /* Linux popen fails with "rb" */ if ((p = popen(line, "r")) == NULL) { #endif D_(D_CRIT "failed"); fclose(t); return -1; } while ((n = fread(buf, 1, BSIZE, p)) > 0) fwrite(buf, 1, n, t); pclose (p); } else { switch (builtin) { case BUILTIN_PP: res = decrunch_pp(*f, t); break; case BUILTIN_ARC: res = decrunch_arc(*f, t); break; case BUILTIN_ARCFS: res = decrunch_arcfs(*f, t); break; case BUILTIN_SQSH: res = decrunch_sqsh(*f, t); break; case BUILTIN_MMCMP: res = decrunch_mmcmp(*f, t); break; case BUILTIN_MUSE: res = decrunch_muse(*f, t); break; case BUILTIN_LZX: res = decrunch_lzx(*f, t); break; case BUILTIN_S404: res = decrunch_s404(*f, t); break; case BUILTIN_ZIP: res = decrunch_zip(*f, t); break; case BUILTIN_GZIP: res = decrunch_gzip(*f, t); break; case BUILTIN_COMPRESS: res = decrunch_compress(*f, t); break; case BUILTIN_BZIP2: res = decrunch_bzip2(*f, t); break; case BUILTIN_XZ: res = decrunch_xz(*f, t); break; case BUILTIN_LHA: res = decrunch_lha(*f, t); break; #if 0 case BUILTIN_ZOO: res = decrunch_zoo(*f, t); break; #endif case BUILTIN_OXM: res = decrunch_oxm(*f, t); break; #ifdef AMIGA case BUILTIN_XFD: res = decrunch_xfd(*f, t); break; #endif } } if (res < 0) { D_(D_CRIT "failed"); fclose(t); return -1; } D_(D_INFO "done"); fclose(*f); *f = t; if (!--ttl) { return -1; } temp2 = strdup(temp->name); res = decrunch(head, f, &temp->name, ttl); free(temp2); /* Mirko: temp is now deallocated in unlink_tempfiles() * not a problem, since unlink_tempfiles() is called after decrunch * in loader routines * * free(temp); */ return res; } /* * Windows doesn't allow you to unlink an open file, so we changed the * temp file cleanup system to remove temporary files after we close it */ static void unlink_tempfiles(struct list_head *head) { struct tmpfilename *li; struct list_head *tmp; /* can't use list_for_each when freeing the node! */ for (tmp = head->next; tmp != head; ) { li = list_entry(tmp, struct tmpfilename, list); D_(D_INFO "unlink tmpfile %s", li->name); unlink(li->name); free(li->name); list_del(&li->list); tmp = tmp->next; free(li); } } int xmp_test_module(char *path, struct xmp_test_info *info) { HIO_HANDLE *h; struct stat st; char buf[XMP_NAME_SIZE]; int i; struct list_head tmpfiles_list; int ret = -XMP_ERROR_FORMAT;; if (stat(path, &st) < 0) return -XMP_ERROR_SYSTEM; #ifndef _MSC_VER if (S_ISDIR(st.st_mode)) { errno = EISDIR; return -XMP_ERROR_SYSTEM; } #endif if ((h = hio_open_file(path, "rb")) == NULL) return -XMP_ERROR_SYSTEM; INIT_LIST_HEAD(&tmpfiles_list); if (decrunch(&tmpfiles_list, &h->f, &path, DECRUNCH_MAX) < 0) { ret = -XMP_ERROR_DEPACK; goto err; } if (hio_stat(h, &st) < 0) {/* get size after decrunch */ ret = -XMP_ERROR_DEPACK; goto err; } if (st.st_size < 256) { /* set minimum valid module size */ ret = -XMP_ERROR_FORMAT; goto err; } if (info != NULL) { *info->name = 0; /* reset name prior to testing */ *info->type = 0; /* reset type prior to testing */ } for (i = 0; format_loader[i] != NULL; i++) { fseek(h->f, 0, SEEK_SET); if (format_loader[i]->test(h, buf, 0) == 0) { int is_prowizard = 0; if (strcmp(format_loader[i]->name, "prowizard") == 0) { fseek(h->f, 0, SEEK_SET); pw_test_format(h->f, buf, 0, info); is_prowizard = 1; } fclose(h->f); unlink_tempfiles(&tmpfiles_list); if (info != NULL && !is_prowizard) { strncpy(info->name, buf, XMP_NAME_SIZE); strncpy(info->type, format_loader[i]->name, XMP_NAME_SIZE); } return 0; } } err: hio_close(h); unlink_tempfiles(&tmpfiles_list); return ret; }
static int mod_test(HIO_HANDLE *f, char *t, const int start) { int i; char buf[4]; struct stat st; int smp_size, num_pat; hio_seek(f, start + 1080, SEEK_SET); if (hio_read(buf, 1, 4, f) < 4) return -1; if (!strncmp(buf + 2, "CH", 2) && isdigit((int)buf[0]) && isdigit((int)buf[1])) { i = (buf[0] - '0') * 10 + buf[1] - '0'; if (i > 0 && i <= 32) { goto found; } } if (!strncmp(buf + 1, "CHN", 3) && isdigit((int)*buf)) { if (*buf - '0') { goto found; } } for (i = 0; mod_magic[i].ch; i++) { if (!memcmp(buf, mod_magic[i].magic, 4)) break; } if (mod_magic[i].ch == 0) return -1; /* * Sanity check to prevent loading NoiseRunner and other module * formats with valid magic at offset 1080 */ hio_seek(f, start + 20, SEEK_SET); for (i = 0; i < 31; i++) { hio_seek(f, 22, SEEK_CUR); /* Instrument name */ if (hio_read16b(f) & 0x8000) /* test length */ return -1; if (hio_read8(f) & 0xf0) /* test finetune */ return -1; if (hio_read8(f) > 0x40) /* test volume */ return -1; if (hio_read16b(f) & 0x8000) /* test loop start */ return -1; if (hio_read16b(f) & 0x8000) /* test loop size */ return -1; } if (HIO_HANDLE_TYPE(f) != HIO_HANDLE_TYPE_FILE) goto found; /* Test for UNIC tracker modules * * From Gryzor's Pro-Wizard PW_FORMATS-Engl.guide: * ``The UNIC format is very similar to Protracker... At least in the * heading... same length : 1084 bytes. Even the "M.K." is present, * sometimes !! Maybe to disturb the rippers.. hehe but Pro-Wizard * doesn't test this only!'' */ /* get file size */ hio_stat(f, &st); smp_size = 0; hio_seek(f, start + 20, SEEK_SET); /* get samples size */ for (i = 0; i < 31; i++) { hio_seek(f, 22, SEEK_CUR); smp_size += 2 * hio_read16b(f); /* Length in 16-bit words */ hio_seek(f, 6, SEEK_CUR); } /* get number of patterns */ num_pat = 0; hio_seek(f, start + 952, SEEK_SET); for (i = 0; i < 128; i++) { uint8 x = hio_read8(f); if (x > 0x7f) break; if (x > num_pat) num_pat = x; } num_pat++; if (start + 1084 + num_pat * 0x300 + smp_size == st.st_size) return -1; found: hio_seek(f, start + 0, SEEK_SET); read_title(f, t, 20); return 0; }