#include #include "../src/hio.h" #include "test.h" #define BUFLEN 16384 void read_file_to_memory(const char *filename, void **_buffer, long *_size) { HIO_HANDLE *f = hio_open(filename, "rb"); void *buffer = NULL; long size; *_buffer = NULL; *_size = 0; if (f == NULL) return; size = hio_size(f); if (size <= 0) { hio_close(f); return; } buffer = malloc(size); if (buffer == NULL) { hio_close(f); return; } if (hio_read(buffer, 1, size, f) != size) { hio_close(f); free(buffer); return; } hio_close(f); *_buffer = buffer; *_size = size; } int check_randomness(int *array, int size, double sdev) { int i; double avg = 0.0; double dev = 0.0; for (i = 0; i < size; i++) { avg += array[i]; } avg /= size; for (i = 0; i < size; i++) { dev += pow(avg - array[i], 2); } dev = sqrt(dev / size); return dev > sdev; } int compare_md5(const unsigned char *d, const char *digest) { int i; /*for (i = 0; i < 16 ; i++) printf("%02x", d[i]); printf("\n");*/ for (i = 0; i < 16 && *digest; i++, digest += 2) { char hex[3]; hex[0] = digest[0]; hex[1] = digest[1]; hex[2] = 0; if (d[i] != strtoul(hex, NULL, 16)) return -1; } return 0; } int check_md5(const char *path, const char *digest) { unsigned char buf[BUFLEN]; unsigned char d[16]; MD5_CTX ctx; FILE *f; int bytes_read; f = fopen(path, "rb"); if (f == NULL) return -1; MD5Init(&ctx); while ((bytes_read = fread(buf, 1, BUFLEN, f)) > 0) { MD5Update(&ctx, buf, bytes_read); } MD5Final(d, &ctx); fclose(f); return compare_md5(d, digest); } int map_channel(struct player_data *p, int chn) { int voc; if ((uint32)chn >= p->virt.virt_channels) return -1; voc = p->virt.virt_channel[chn].map; if ((uint32)voc >= p->virt.maxvoc) return -1; return voc; } /* Convert little-endian 16 bit samples to big-endian */ void convert_endian(unsigned char *p, int l) { uint8 b; int i; for (i = 0; i < l; i++) { b = p[0]; p[0] = p[1]; p[1] = b; p += 2; } } static int read_line(char *line, int size, FILE *f) { int pos; if (!fgets(line, size, f)) { line[0] = '\0'; return 0; } pos = strlen(line); if (pos > 0 && line[pos - 1] == '\n') line[--pos] = 0; return pos; } static void check_envelope(struct xmp_envelope *env, char *line, FILE *f) { int i, x; char *s; /* read envelope parameters */ read_line(line, 1024, f); x = strtoul(line, &s, 0); fail_unless(x == env->flg, "envelope flags"); x = strtoul(s, &s, 0); fail_unless(x == env->npt, "envelope number of points"); x = strtoul(s, &s, 0); fail_unless(x == env->scl, "envelope scaling"); x = strtoul(s, &s, 0); fail_unless(x == env->sus, "envelope sustain start"); x = strtoul(s, &s, 0); fail_unless(x == env->sue, "envelope sustain end"); x = strtoul(s, &s, 0); fail_unless(x == env->lps, "envelope loop start"); x = strtoul(s, &s, 0); fail_unless(x == env->lpe, "envelope loop end"); if (env->npt > 0) { read_line(line, 1024, f); s = line; for (i = 0; i < env->npt * 2; i++) { x = strtoul(s, &s, 0); fail_unless(x == env->data[i], "envelope point"); } } } int compare_module(struct xmp_module *mod, FILE *f) { char line[1024]; char *s; int i, j, x; /* Check title and format */ read_line(line, 1024, f); fail_unless(!strcmp(line, mod->name), "module name"); read_line(line, 1024, f); fail_unless(!strcmp(line, mod->type), "module type"); /* Check module attributes */ read_line(line, 1024, f); x = strtoul(line, &s, 0); fail_unless(x == mod->pat, "number of patterns"); x = strtoul(s, &s, 0); fail_unless(x == mod->trk, "number of tracks"); x = strtoul(s, &s, 0); fail_unless(x == mod->chn, "number of channels"); x = strtoul(s, &s, 0); fail_unless(x == mod->ins, "number of instruments"); x = strtoul(s, &s, 0); fail_unless(x == mod->smp, "number of samples"); x = strtoul(s, &s, 0); fail_unless(x == mod->spd, "initial speed"); x = strtoul(s, &s, 0); fail_unless(x == mod->bpm, "initial tempo"); x = strtoul(s, &s, 0); fail_unless(x == mod->len, "module length"); x = strtoul(s, &s, 0); fail_unless(x == mod->rst, "restart position"); x = strtoul(s, &s, 0); fail_unless(x == mod->gvl, "global volume"); /* Check orders */ if (mod->len > 0) { read_line(line, 1024, f); s = line; for (i = 0; i < mod->len; i++) { x = strtoul(s, &s, 0); fail_unless(x == mod->xxo[i], "orders"); } } /* Check instruments */ for (i = 0; i < mod->ins; i++) { struct xmp_instrument *xxi = &mod->xxi[i]; read_line(line, 1024, f); x = strtoul(line, &s, 0); fail_unless(x == xxi->vol, "instrument volume"); x = strtoul(s, &s, 0); fail_unless(x == xxi->nsm, "number of subinstruments"); x = strtoul(s, &s, 0); fail_unless(x == xxi->rls, "instrument release"); x = strncmp(++s, xxi->name, 32); fail_unless(x == 0, "instrument name"); /* check envelopes */ check_envelope(&xxi->aei, line, f); check_envelope(&xxi->fei, line, f); check_envelope(&xxi->pei, line, f); /* check mapping */ read_line(line, 1024, f); s = line; for (j = 0; j < XMP_MAX_KEYS; j++) { x = strtoul(s, &s, 0); fail_unless(x == xxi->map[j].ins, "instrument map"); } read_line(line, 1024, f); s = line; for (j = 0; j < XMP_MAX_KEYS; j++) { x = strtoul(s, &s, 0); fail_unless(x == xxi->map[j].xpo, "transpose map"); } /* check subinstruments */ for (j = 0; j < xxi->nsm; j++) { struct xmp_subinstrument *sub = &xxi->sub[j]; read_line(line, 1024, f); x = strtoul(line, &s, 0); fail_unless(x == sub->vol, "subinst volume"); x = strtoul(s, &s, 0); fail_unless(x == sub->gvl, "subinst gl volume"); x = strtoul(s, &s, 0); fail_unless(x == sub->pan, "subinst pan"); x = strtoul(s, &s, 0); fail_unless(x == sub->xpo, "subinst transpose"); x = strtoul(s, &s, 0); fail_unless(x == sub->fin, "subinst finetune"); x = strtoul(s, &s, 0); fail_unless(x == sub->vwf, "subinst vibr wf"); x = strtoul(s, &s, 0); fail_unless(x == sub->vde, "subinst vibr depth"); x = strtoul(s, &s, 0); fail_unless(x == sub->vra, "subinst vibr rate"); x = strtoul(s, &s, 0); fail_unless(x == sub->vsw, "subinst vibr sweep"); x = strtoul(s, &s, 0); fail_unless(x == sub->rvv, "subinst vol var"); x = strtoul(s, &s, 0); fail_unless(x == sub->sid, "subinst sample nr"); x = strtoul(s, &s, 0); fail_unless(x == sub->nna, "subinst NNA"); x = strtoul(s, &s, 0); fail_unless(x == sub->dct, "subinst DCT"); x = strtoul(s, &s, 0); fail_unless(x == sub->dca, "subinst DCA"); x = strtoul(s, &s, 0); fail_unless(x == sub->ifc, "subinst cutoff"); x = strtoul(s, &s, 0); fail_unless(x == sub->ifr, "subinst resonance"); } } /* Check patterns */ for (i = 0; i < mod->pat; i++) { struct xmp_pattern *xxp = mod->xxp[i]; read_line(line, 1024, f); x = strtoul(line, &s, 0); fail_unless(x == xxp->rows, "pattern rows"); for (j = 0; j < mod->chn; j++) { x = strtoul(s, &s, 0); fail_unless(x == xxp->index[j], "pattern index"); } } /* Check tracks */ for (i = 0; i < mod->trk; i++) { struct xmp_track *xxt = mod->xxt[i]; unsigned char d[16]; MD5_CTX ctx; read_line(line, 1024, f); x = strtoul(line, &s, 0); fail_unless(x == xxt->rows, "track rows"); MD5Init(&ctx); MD5Update(&ctx, (const unsigned char *)xxt->event, xxt->rows * sizeof (struct xmp_event)); MD5Final(d, &ctx); fail_unless(compare_md5(d, ++s) == 0, "track data"); } /* Check samples */ for (i = 0; i < mod->smp; i++) { struct xmp_sample *xxs = &mod->xxs[i]; unsigned char d[16]; MD5_CTX ctx; int len = xxs->len; if (xxs->flg & XMP_SAMPLE_16BIT) { len *= 2; /* Normalize data to little endian for the hash. */ if (is_big_endian()) { convert_endian(xxs->data, xxs->len); } } read_line(line, 1024, f); x = strtoul(line, &s, 0); fail_unless(x == xxs->len, "sample length"); x = strtoul(s, &s, 0); fail_unless(x == xxs->lps, "sample loop start"); x = strtoul(s, &s, 0); fail_unless(x == xxs->lpe, "sample loop end"); x = strtoul(s, &s, 0); fail_unless(x == xxs->flg, "sample flags"); s++; if (len > 0 && xxs->data != NULL) { MD5Init(&ctx); MD5Update(&ctx, xxs->data, len); MD5Final(d, &ctx); fail_unless(compare_md5(d, s) == 0, "sample data"); } s += 32; fail_unless(strcmp(xxs->name, ++s) == 0, "sample name"); } /* Check channels */ for (i = 0; i < mod->chn; i++) { struct xmp_channel *xxc = &mod->xxc[i]; read_line(line, 1024, f); x = strtoul(line, &s, 0); fail_unless(x == xxc->pan, "channel pan"); x = strtoul(s, &s, 0); fail_unless(x == xxc->vol, "channel volume"); x = strtoul(s, &s, 0); fail_unless(x == xxc->flg, "channel flags"); } return 0; } static void dump_envelope(struct xmp_envelope *env, FILE *f) { int i; /* dump envelope parameters */ fprintf(f, "%d %d %d %d %d %d %d\n", env->flg, /* envelope flags */ env->npt, /* envelope number of points */ env->scl, /* envelope scaling */ env->sus, /* envelope sustain start */ env->sue, /* envelope sustain end */ env->lps, /* envelope loop start */ env->lpe /* envelope loop end */ ); if (env->npt > 0) { for (i = 0; i < env->npt * 2; i++) { fprintf(f, "%s%d", i ? " " : "", env->data[i]); } fprintf(f, "\n"); } } void dump_module(struct xmp_module *mod, FILE *f) { int i, j; /* Write title and format */ fprintf(f, "%s\n", mod->name); fprintf(f, "%s\n", mod->type); /* Write module attributes */ fprintf(f, "%u %u %u %u %u %u %u %u %u %u\n", mod->pat, /* number of patterns */ mod->trk, /* number of tracks */ mod->chn, /* number of channels */ mod->ins, /* number of instruments */ mod->smp, /* number of samples */ mod->spd, /* initial speed */ mod->bpm, /* initial tempo */ mod->len, /* module length */ mod->rst, /* restart position */ mod->gvl /* global volume */ ); /* Write orders */ for (i = 0; i < mod->len; i++) { fprintf(f, "%s%u", i ? " " : "", mod->xxo[i]); } fprintf(f, "\n"); /* Write instruments */ for (i = 0; i < mod->ins; i++) { struct xmp_instrument *xxi = &mod->xxi[i]; fprintf(f, "%u %u %u %s\n", xxi->vol, /* instrument volume */ xxi->nsm, /* number of subinstruments */ xxi->rls, /* instrument release */ xxi->name /* instrument name */ ); /* Write envelopes */ dump_envelope(&xxi->aei, f); dump_envelope(&xxi->fei, f); dump_envelope(&xxi->pei, f); /* Write mapping */ for (j = 0; j < XMP_MAX_KEYS; j++) { fprintf(f, "%s%d", j ? " " : "", xxi->map[j].ins); } fprintf(f, "\n"); for (j = 0; j < XMP_MAX_KEYS; j++) { fprintf(f, "%s%d", j ? " " : "", xxi->map[j].xpo); } fprintf(f, "\n"); /* Write subinstruments */ for (j = 0; j < xxi->nsm; j++) { struct xmp_subinstrument *sub = &xxi->sub[j]; fprintf(f, "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n", sub->vol, /* subinst volume */ sub->gvl, /* subinst gl volume */ sub->pan, /* subinst pan */ sub->xpo, /* subinst transpose */ sub->fin, /* subinst finetune */ sub->vwf, /* subinst vibr wf */ sub->vde, /* subinst vibr depth */ sub->vra, /* subinst vibr rate */ sub->vsw, /* subinst vibr sweep */ sub->rvv, /* subinst vol var */ sub->sid, /* subinst sample nr */ sub->nna, /* subinst NNA */ sub->dct, /* subinst DCT */ sub->dca, /* subinst DCA */ sub->ifc, /* subinst cutoff */ sub->ifr /* subinst resonance */ ); } } /* Write patterns */ for (i = 0; i < mod->pat; i++) { struct xmp_pattern *xxp = mod->xxp[i]; fprintf(f, "%u", xxp->rows); /* pattern rows */ for (j = 0; j < mod->chn; j++) { fprintf(f, " %u", xxp->index[j]); /* pattern index */ } fprintf(f, "\n"); } /* Write tracks */ for (i = 0; i < mod->trk; i++) { struct xmp_track *xxt = mod->xxt[i]; unsigned char d[MD5_DIGEST_LENGTH]; MD5_CTX ctx; MD5Init(&ctx); MD5Update(&ctx, (const unsigned char *)xxt->event, xxt->rows * sizeof (struct xmp_event)); MD5Final(d, &ctx); fprintf(f, "%u ", xxt->rows); /* track rows */ for (j = 0; j < MD5_DIGEST_LENGTH; j++) { fprintf(f, "%02x", d[j]); /* track data */ } fprintf(f, "\n"); } /* Write samples */ for (i = 0; i < mod->smp; i++) { struct xmp_sample *xxs = &mod->xxs[i]; unsigned char d[MD5_DIGEST_LENGTH]; MD5_CTX ctx; int len = xxs->len; if (xxs->flg & XMP_SAMPLE_16BIT) { len *= 2; /* Normalize data to little endian for the hash. */ if (is_big_endian()) { convert_endian(xxs->data, xxs->len); } } fprintf(f, "%d %d %d %d", xxs->len, /* sample length */ xxs->lps, /* sample loop start */ xxs->lpe, /* sample loop end */ xxs->flg /* sample flags */ ); MD5Init(&ctx); if (len > 0 && xxs->data != NULL) { MD5Update(&ctx, xxs->data, len); } else if (len > 0) { /* The data file for fracture.stm had this hash for empty samples... */ MD5Update(&ctx, (unsigned char *)"1", 1); } MD5Final(d, &ctx); fprintf(f, " "); for (j = 0; j < MD5_DIGEST_LENGTH; j++) { fprintf(f, "%02x", d[j]); /* sample data */ } fprintf(f, " %s\n", xxs->name); /* sample name */ } /* Write channels */ for (i = 0; i < mod->chn; i++) { struct xmp_channel *xxc = &mod->xxc[i]; fprintf(f, "%u %u %u\n", xxc->pan, /* channel pan */ xxc->vol, /* channel volume */ xxc->flg /* channel flags */ ); } } void compare_playback(const char *filename, const struct playback_sequence *sequence, int rate, int flags, int interp) { xmp_context opaque; int count, ret; opaque = xmp_create_context(); fail_unless(opaque, "create context"); ret = xmp_load_module(opaque, filename); fail_unless(ret == 0, "module load"); ret = xmp_start_player(opaque, rate, flags); fail_unless(ret == 0, "start player"); ret = xmp_set_player(opaque, XMP_PLAYER_INTERP, interp); fail_unless(ret == 0, "set interp"); while (sequence->action != PLAY_END) { switch (sequence->action) { case PLAY_END: /* silence warning */ break; case PLAY_FRAMES: for (count = sequence->value; count > 0; count--) ret = xmp_play_frame(opaque); fail_unless(ret == sequence->result, "play frames"); break; } sequence++; } xmp_free_context(opaque); }