#include #include #include #include #include #include #include #include #include #include "ayemu.h" //#define trace(...) { fprintf (stderr, __VA_ARGS__); } #define trace(fmt,...) #define AYEMU_VTX_STRING_MAX 254 typedef const char * data_ptr_t; /* LHA5 decoder, defined in lh5dec.c */ extern void lh5_decode(unsigned const char *inp, unsigned char *outp, unsigned long original_size, unsigned long packed_size); /* Read 8-bit integer from file. */ static data_ptr_t read_byte(data_ptr_t pos, int *p) { const unsigned char *data = (const unsigned char*)pos; *p = *data++; return (data_ptr_t)data; } /* Read 16-bit little-endian 1234 integer from file. */ static data_ptr_t read_word16(data_ptr_t pos, int *p) { const unsigned char *data = (const unsigned char*)pos; *p = *data++; *p += *data++ << 8; return (data_ptr_t)data; } /* read 32-bit integer from file. */ static data_ptr_t read_word32(data_ptr_t pos, int *p) { const unsigned char *data = (const unsigned char*)pos; *p = *data++; *p += *data++ << 8; *p += *data++ << 16; *p += *data++ << 24; return (data_ptr_t)data; } /* read_string: max 254 chars len (+1 for null terminator). */ static data_ptr_t read_string(data_ptr_t pos, char **res) { int len; if (pos == NULL) return NULL; len = strlen(pos); if (len > AYEMU_VTX_STRING_MAX) { fprintf(stderr, "Error: string len more than %d (=%d)\n", AYEMU_VTX_STRING_MAX, len); return NULL; } *res = calloc(1, len + 1); strcpy(*res, pos); return pos + len + 1; } static data_ptr_t read_header(data_ptr_t buf, ayemu_vtx_t **target, size_t size) { char hdr[3]; ayemu_vtx_t *vtx; hdr[0] = tolower(*buf++); hdr[1] = tolower(*buf++); hdr[2] = '\0'; if (size < 20) { fprintf(stderr, "ayemu_vtx_open: file size < 20 bytes - it is impossible\n"); return NULL; } vtx = (ayemu_vtx_t*) calloc(1, sizeof(ayemu_vtx_t)); if (strncmp(hdr, "ay", 2) == 0) vtx->chiptype = AYEMU_AY; else if (strncmp (hdr, "ym", 2) == 0) vtx->chiptype = AYEMU_YM; else { trace ("File is _not_ VORTEX format!\n" "It not begins with 'ay' or 'ym' string.\n"); goto clean_and_ret_null; } buf = read_byte(buf, &vtx->stereo); buf = read_word16(buf, &vtx->loop); buf = read_word32(buf, &vtx->chipFreq); buf = read_byte(buf, &vtx->playerFreq); buf = read_word16(buf, &vtx->year); buf = read_word32(buf, &vtx->regdata_size); buf = read_string(buf, &vtx->title); buf = read_string(buf, &vtx->author); buf = read_string(buf, &vtx->from); buf = read_string(buf, &vtx->tracker); buf = read_string(buf, &vtx->comment); *target = vtx; return buf; clean_and_ret_null: ayemu_vtx_free(vtx); return NULL; } ayemu_vtx_t * ayemu_vtx_header(data_ptr_t buf, size_t size) { ayemu_vtx_t *vtx = NULL; const char *data; data = read_header(buf, &vtx, size); return vtx; } ayemu_vtx_t * ayemu_vtx_load(data_ptr_t buf, size_t size) { ayemu_vtx_t *vtx; const char *data = read_header(buf, &vtx, size); if (! data) { fprintf(stderr, "ayemu_vtx_load: Cannot parse file header\n"); return NULL; } // unpack data size -= (buf - data); if ((vtx->regdata = (unsigned char *) malloc (vtx->regdata_size)) == NULL) { fprintf (stderr, "ayemu_vtx_load_data: Can allocate %d bytes" " for unpack register data\n", vtx->regdata_size); return NULL; } lh5_decode ((unsigned char*)data, vtx->regdata, vtx->regdata_size, size); vtx->frames = vtx->regdata_size / 14; return vtx; } /** Get specified data frame by number. * */ void ayemu_vtx_getframe(const ayemu_vtx_t *vtx, size_t frame_n, ayemu_ay_reg_frame_t regs) { int n; if (frame_n >= vtx->frames) return; // calculate begin of data unsigned char *p = vtx->regdata + frame_n; // step is data size / 14 for(n = 0 ; n < 14 ; n++) { regs[n] = *p; p += vtx->frames; } } /** Free all allocaded resources. * */ void ayemu_vtx_free(ayemu_vtx_t *vtx) { #define FREE_PTR(x) if (x) { free(x); x = NULL; } if (vtx) { FREE_PTR(vtx->title); FREE_PTR(vtx->author); FREE_PTR(vtx->from); FREE_PTR(vtx->tracker); FREE_PTR(vtx->comment); FREE_PTR(vtx->regdata); free (vtx); } } ayemu_vtx_t * ayemu_vtx_header_from_file(const char *filename) { ayemu_vtx_t *ret; size_t size; const size_t page_size = (size_t) sysconf (_SC_PAGESIZE); int fd; struct stat st; // printf("Page size is %d\n", page_size); if (stat(filename, &st) != 0) { fprintf(stderr, "Can't stat file %s: %s\n", filename, strerror(errno)); return NULL; } size = st.st_size; fd = open(filename, O_RDONLY, 0); if (fd == 0) { fprintf(stderr, "Can't open file %s: %s\n", filename, strerror(errno)); return NULL; } size_t data_len = (size / page_size + 1) * page_size; char *data = mmap(NULL, data_len, PROT_READ, MAP_PRIVATE, fd, 0); if (data == (void*)(-1)) { fprintf(stderr, "Can't mmap file %s: %s\n", filename, strerror(errno)); return NULL; } ret = ayemu_vtx_header(data, size); if (munmap(data, data_len) != 0) { fprintf(stderr, "Can't munmmap file %s: %s\n", filename, strerror(errno)); } return ret; } ayemu_vtx_t * ayemu_vtx_load_from_file(const char *filename) { size_t size; const size_t page_size = (size_t) sysconf (_SC_PAGESIZE); int fd; struct stat st; ayemu_vtx_t *ret; // printf("Page size is %d\n", page_size); if (stat(filename, &st) != 0) { fprintf(stderr, "Can't stat file %s: %s\n", filename, strerror(errno)); return NULL; } size = st.st_size; fd = open(filename, O_RDONLY, 0); if (fd == 0) { fprintf(stderr, "Can't open file %s: %s\n", filename, strerror(errno)); return NULL; } size_t data_len = (size / page_size + 1) * page_size; char *data = mmap(NULL, data_len, PROT_READ, MAP_PRIVATE, fd, 0); if (data == (void*)(-1)) { fprintf(stderr, "Can't mmap file %s: %s\n", filename, strerror(errno)); return NULL; } ret = ayemu_vtx_load(data, size); if (munmap(data, data_len) != 0) { fprintf(stderr, "Can't munmmap file %s: %s\n", filename, strerror(errno)); } return ret; }