diff options
Diffstat (limited to 'plugins/vtx/vtxfile.c')
-rw-r--r-- | plugins/vtx/vtxfile.c | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/plugins/vtx/vtxfile.c b/plugins/vtx/vtxfile.c new file mode 100644 index 00000000..9dbeb9f7 --- /dev/null +++ b/plugins/vtx/vtxfile.c @@ -0,0 +1,283 @@ +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <assert.h> +#include <ctype.h> +#include <unistd.h> + +#include <sys/mman.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include "ayemu.h" + +#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 { + fprintf (stderr, "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; + + 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; } + + FREE_PTR(vtx->title); + FREE_PTR(vtx->author); + FREE_PTR(vtx->from); + FREE_PTR(vtx->tracker); + FREE_PTR(vtx->comment); + FREE_PTR(vtx->regdata); +} + + + + +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; +} |