/* ************************************************************************** */ /* _____ _ */ /* parse/mcs.c |_ _|__ _ _| |__ ___ _ _ */ /* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */ /* | | (_) | |_| | | | | __/ |_| | */ /* By: thomas |_|\___/ \__,_|_| |_|\___|\__, |.fr */ /* Last updated: 2016/12/01 13:33:45 |___/ */ /* */ /* ************************************************************************** */ #include #include /* ************************************************************************** */ /* Internal functions */ /* ************************************************************************** */ /** * get_image: * Allocate space, and convert monochromic image to 0x0RGB. * * @arg stream the stream from which to read. * @arg img the image to make. * @arg width the image width. * @arg height the image height. */ static int get_image(FILE *stream, uint32_t ***img, unsigned int width, unsigned int height) { /* get raw pixels */ size_t linesize = width / 8 + !!(width % 8); size_t bufsize = height * linesize; uint8_t buf[bufsize]; READ(buf, bufsize) /* alloc */ *img = malloc(height * (sizeof(uint32_t*)) + width * height * sizeof(uint32_t)); if (!*img) return (g1m_error_alloc); /* fill */ uint32_t **image = *img; for (uint_fast16_t y = 0; y < height; y++) { /* setup index */ image[y] = (uint32_t*)&image[height] + y * width; /* init vars for monochrome with fill bits image browsing */ uint8_t *b = &buf[y * linesize]; int bit = 1 << 7; /* browse and save pixels! (monochrome to 0x0RGB) */ for (uint_fast16_t x = 0; x < width; x++) { /* set pixel */ image[y][x] = *b & bit ? 0xffffff : 0x000000; /* go further */ b += bit & 1; bit = (bit >> 1) | ((bit & 1) << 7); } } /* no error */ return (0); } /* ************************************************************************** */ /* MCS file parsing */ /* ************************************************************************** */ /** * mcs_parse_program: * Parse a program. * * @arg handle the handle. * @arg stream the stream to parse from. * @arg length the data length. * @return the error code (0 if ok). */ static int mcs_parse_program(g1m_mcsfile_t *handle, FILE *stream, uint_fast32_t length) { /* read header */ DREAD(hd, mcs_programheader) /* print header data */ log_info("Program password is '%.8s'.", hd.password); /* store info */ strncpy(handle->password, (char*)hd.password, 8); handle->password[8] = 0; /* get content */ size_t content_size = length - sizeof(struct mcs_programheader); log_info("Getting program content (%" PRIuSIZE "o)", content_size); handle->content = malloc(content_size + 1); handle->content_size = content_size + 1; if (!handle->content) return (g1m_error_alloc); READ(handle->content, content_size); handle->content[content_size] = 0; /* no error */ return (0); } /** * mcs_parse_spreadsheet: * Parse a spreadsheet. * * @arg handle the handle. * @arg stream the stream to parse from. * @arg length the data length. * @return the error code (0 if ok). */ static int mcs_parse_spreadsheet(g1m_mcsfile_t *handle, FILE *stream, uint_fast32_t length) { /* read header */ DREAD(hd, mcs_spreadsheetheader) /* check if the spreadsheet type is known */ if (hd.has_subheader != 0x01) return (0); /* read subheader */ DREAD(shd, mcs_spreadsheet_subheader) /* correct endianness */ uint_fast32_t colcount = hd.column_count; colcount = be32toh(colcount << 8); shd.defs_size = be32toh(shd.defs_size); /* prepare */ g1m_mcs_cell_t cells[1000 * colcount]; bzero(cells, sizeof(g1m_mcs_cell_t) * 1000 * colcount); int cells_count = 0; uint_fast32_t rows = 0; uint_fast32_t cols = 0; /* log some info */ log_info("%" PRIuFAST32 " columns to parse!", colcount); if (colcount) { /* get the column directory */ uint32_t column_directory[colcount]; READ(&column_directory, sizeof(uint32_t) * colcount) /* browse columns */ for (uint_fast32_t c = 0; c < colcount; c++) { /* check if column is empty */ if (!column_directory[c]) continue; /* get the row directory */ uint8_t row_directory[0x80]; READ(&row_directory, (size_t)0x80) /* initialize loop values */ uint8_t *rd = row_directory; int bit = 1 << 7; /* explore each cell */ for (uint_fast32_t i = 0; i < 1000; i++) { /* check if used */ if (*rd & bit) { /* get cell */ DREAD(cell, bcd) /* store it */ cells[c * 1000 + i] = (g1m_mcs_cell_t){ .used = 1, .real = cell, .imgn = {} }; /* check things (max row, max col, cells count) */ rows = max(rows, i); cols = c; cells_count++; /* log it */ char *bcd_string = g1m_bcdtoa(&cell); log_info("[%" PRIuFAST32 ", %" PRIuFAST32 "] contains '%s'.", c, i, bcd_string); free(bcd_string); } /* iterate bit and rd */ rd += (bit & 1); bit = (bit >> 1) | ((bit & 1) << 7); } } } /* we have max rows and columns, increment to have sizes */ rows++, cols++; /* create final tab */ handle->cells = NULL; handle->columns = 0; handle->rows = 0; if (cells_count) { handle->columns = cols; handle->rows = rows; /* alloc */ g1m_mcs_cell_t **tab = malloc(sizeof(g1m_mcs_cell_t*) * cols); if (!tab) return (g1m_error_alloc); g1m_mcs_cell_t *rws = malloc(sizeof(g1m_mcs_cell_t) * cols * rows); if (!rws) { free(tab); return (g1m_error_alloc); } /* main copying loop */ for (uint_fast32_t y = 0; y < cols; y++) { /* prepare index */ tab[y] = &rws[y * rows]; /* copy each cell */ for (uint_fast32_t x = 0; x < rows; x++) tab[y][x] = cells[x * 1000 + y]; } /* i used to be a cow. */ handle->cells = tab; } /* no error */ return (0); } /** * mcs_parse_list: * Parse an List. * * @arg handle the handle. * @arg stream the stream to parse from. * @arg size the length. * @return the error code (0 if ok). */ static int mcs_parse_list(g1m_mcsfile_t *handle, FILE *stream, uint_fast32_t size) { /* read header */ DREAD(hd, mcs_listheader) /* correct endianess */ uint_fast16_t elcount = be16toh(hd.element_count); /* log */ log_info("Title is '%.8s'", hd.title); /* prepare browsing */ size -= sizeof(struct mcs_listheader); /* make tabs */ size_t elsize = sizeof(struct bcd) * elcount; struct bcd real[elcount]; struct bcd imgn[elcount]; if (elcount) { /* browse real elements */ log_info("Browsing %" PRIuFAST16 " list elements", elcount); READ(&real, elsize) size -= elsize; /* check if there is one imaginary number */ int one_imgn = 0; for (uint_fast16_t i = 0; i < elcount && !one_imgn; i++) one_imgn |= g1m_bcd_has_complex(&real[i]); /* browse imaginary elements */ if (one_imgn) { log_info("They have complex parts!"); size_t toread = min(size, elsize); READ(&imgn, toread) } } /* fill final tab */ handle->rows = elcount; handle->columns = 1; handle->cells = NULL; if (elcount) { /* allocate final tab */ g1m_mcs_cell_t **tab = malloc(sizeof(g1m_mcs_cell_t*) * elcount); if (!tab) return (g1m_error_alloc); g1m_mcs_cell_t *rws = malloc(sizeof(g1m_mcs_cell_t) * elcount); if (!tab) { free(tab); return (g1m_error_alloc); } /* main copying loop */ for (uint_fast32_t y = 0; y < elcount; y++) { int has_complex = g1m_bcd_has_complex(&real[y]); /* log */ #if LOGLEVEL <= ll_info char *real_string = g1m_bcdtoa(&real[y]); if (!has_complex) log_info("Cell #%" PRIuFAST32 " is (%s).", y, real_string); else { char *imgn_string = g1m_bcdtoa(&imgn[y]); log_info("Cell #%" PRIuFAST32 " is (%s)+(%s)i.", y, real_string, imgn_string); free(imgn_string); } free(real_string); #endif /* prepare index and store */ tab[y] = &rws[y]; tab[y][0] = (g1m_mcs_cell_t){ .used = 1, .real = real[y], .imgn = has_complex ? imgn[y] : (struct bcd){} }; } /* don't forget your baguette! */ handle->cells = tab; } /* no error */ return (0); } /** * mcs_parse_matrix: * Parse a matrix. * * @arg handle the handle. * @arg stream the stream to parse from. * @arg length the data length. * @return the error code (0 if ok). */ static int mcs_parse_matrix(g1m_mcsfile_t *handle, FILE *stream, uint_fast32_t length) { g1m_mcs_cell_t **tab, *rws; int err = g1m_error_alloc; /* read header */ DREAD(hd, mcs_matheader) /* correct endianess */ hd.width = be16toh(hd.width); hd.height = be16toh(hd.height); /* log info */ uint_fast32_t w = hd.width, h = hd.height; log_info("Matrix size is %" PRIuFAST32 "*%" PRIuFAST32, w, h); /* store dimensions */ handle->rows = w; handle->columns = h; handle->cells = NULL; if (w && h) { /* alloc real matrix */ tab = malloc(sizeof(g1m_mcs_cell_t*) * h); if (!tab) return (g1m_error_alloc); rws = malloc(sizeof(g1m_mcs_cell_t) * h * w); if (!rws) { free(tab); return (g1m_error_alloc); } /* copy */ int one_imgn = 0; for (uint_fast32_t y = 0; y < h; y++) { /* prepare index */ tab[y] = &rws[y * w]; /* read squares */ for (uint_fast32_t x = 0; x < w; x++) { /* read the cell */ GDREAD(cell, bcd) one_imgn |= g1m_bcd_has_complex(&cell); /* store it */ tab[y][x] = (g1m_mcs_cell_t){ .real = cell, .imgn = {}, .used = 1 }; } } /* check imaginary parts */ if (one_imgn) { for (uint_fast32_t y = 0; y < h; y++) for (uint_fast32_t x = 0; x < w; x++) { GDREAD(cell, bcd) if (g1m_bcd_has_complex(&tab[y][x].real)) tab[y][x].imgn = cell; } } /* your sandwich, monsieur. */ handle->cells = tab; } #if LOGLEVEL <= ll_info for (uint_fast32_t y = 0; y < h; y++) for (uint_fast32_t x = 0; x < w; x++) { g1m_mcs_cell_t *cell = &tab[y][x]; char *real_string = g1m_bcdtoa(&cell->real); if (!g1m_bcd_has_complex(&cell->real)) log_info("[%" PRIuFAST32 "][%" PRIuFAST32 "] (%s).", x, y, real_string); else { char *imgn_string = g1m_bcdtoa(&cell->imgn); log_info("[%" PRIuFAST32 "][%" PRIuFAST32 "] (%s)+(%s)i.", x, y, real_string, imgn_string); free(imgn_string); } free(real_string); } #endif /* no error */ return (0); /* in case of unexpected EOF */ fail: free(tab); free(rws); return (err); } /** * mcs_parse_capture: * Parse a capture. * * @arg handle the handle. * @arg stream the stream to parse from. * @arg length the data length. * @return the error code (0 if ok). */ static int mcs_parse_capture(g1m_mcsfile_t *handle, FILE *stream, uint_fast32_t length) { /* read header */ DREAD(hd, mcs_captureheader) /* correct endianess */ hd.width = be16toh(hd.width); hd.height = be16toh(hd.height); /* store */ handle->width = hd.width; handle->height = hd.height; /* print info */ log_info("capture is %dx%d sized", handle->width, handle->height); /* get the image and return */ return (get_image(stream, &handle->image, handle->width, handle->height)); } /** * mcs_parse_picture: * Parse a picture. * * @arg handle the handle. * @arg stream the stream to parse from. * @arg length the data length. * @return the error code (0 if ok). */ static int mcs_parse_picture(g1m_mcsfile_t *handle, FILE *stream, uint_fast32_t length) { int err; /* store some dimensions, in case. */ handle->width = 128; handle->height = 64; /* get the first image */ if ((err = get_image(stream, &handle->image, 128, 64))) { log_fatal("Failed to get the first image."); return (err); } /* and the second */ if ((err = get_image(stream, &handle->second_image, 128, 64))) { log_fatal("Failed to get the second image."); return (err); } /* no error */ return (0); } /** * mcs_parse_string: * Parse string. * * @arg handle the handle. * @arg stream the stream to parse from. * @arg length the data length. * @return the error code (0 if ok). */ static int mcs_parse_string(g1m_mcsfile_t *handle, FILE *stream, uint_fast32_t length) { (void)handle; log_info("String MCS file is not managed yet. Content:"); uint8_t str[length]; READ(str, length); logm_info(str, length); /* TODO */ return (0); } /** * mcs_parse_setup: * Parse settings. * * @arg handle the handle. * @arg stream the stream to parse from. * @arg length the data length. * @return the error code (0 if ok). */ static int mcs_parse_setup(g1m_mcsfile_t *handle, FILE *stream, uint_fast32_t length) { (void)handle; log_info("Settings MCS file is not managed yet."); /* TODO */ SKIP(length) return (0); } /** * mcs_parse_alphamem: * Parse alphamem. * * @arg handle the handle. * @arg stream the stream to parse from. * @arg length the data length. * @return the error code (0 if ok). */ static int mcs_parse_alphamem(g1m_mcsfile_t *handle, FILE *stream, uint_fast32_t length) { /* read the data */ uint8_t buf[length]; READ(buf, length) /* allocate the vars table */ handle->vars = malloc(29 * sizeof(g1m_mcs_cell_t)); if (!handle->vars) return (g1m_error_alloc); bzero(handle->vars, 29 * sizeof(g1m_mcs_cell_t)); /* copy */ size_t tocopy = length - length % sizeof(g1m_mcs_cell_t); memcpy(handle->vars, buf, tocopy); /* log */ #if LOGLEVEL <= ll_inf for (int i = 0; i < 29; i++) { const char *var_name = (const char*[]){ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "Theta", "r", "Ans" }[i]; char *real_string = g1m_bcdtoa(&handle->vars[i].real); if (!g1m_bcd_has_complex(&handle->vars[i].real)) log_info("%s is (%s).", var_name, real_string); else { char *imgn_string = g1m_bcdtoa(&handle->vars[i].imgn); log_info("%s is (%s)+(%s)i.", var_name, real_string, imgn_string); free(imgn_string); } free(real_string); } #endif /* no problem, woop woop */ return (0); } /* ************************************************************************** */ /* Type correspondance list */ /* ************************************************************************** */ /* MCS file parsing function type */ typedef int (*mcs_parse_func_t)(g1m_mcsfile_t*, FILE*, uint_fast32_t); /* Correspondance type */ struct mcs_corresp { unsigned int type; mcs_parse_func_t parse; }; /* All correspondances */ static struct mcs_corresp mcs_types[] = { {g1m_mcstype_program, mcs_parse_program,}, {g1m_mcstype_list, mcs_parse_list}, {g1m_mcstype_mat, mcs_parse_matrix}, {g1m_mcstype_vct, mcs_parse_matrix}, {g1m_mcstype_spreadsheet, mcs_parse_spreadsheet}, {g1m_mcstype_pict, mcs_parse_picture}, {g1m_mcstype_capt, mcs_parse_capture}, {g1m_mcstype_string, mcs_parse_string}, {g1m_mcstype_setup, mcs_parse_setup}, {g1m_mcstype_alphamem, mcs_parse_alphamem}, {} }; /** * lookup_mcsfile_parse: * Lookup for a parsing function for an MCS file type. * * @arg type the libg1m MCS file type. * @return the function (NULL if not found). */ static mcs_parse_func_t lookup_mcsfile_parse(unsigned int type) { /* lookup for the type */ struct mcs_corresp *c = mcs_types; while (c->parse) { if (type == c->type) break; c++; } /* return the function */ return (c->parse); } /* ************************************************************************** */ /* Public API functions */ /* ************************************************************************** */ /** * g1m_parse_mcsfile_content: * Parse MCS file content. * * Part of the public API because of the Protocol 7, where file is sent * without its header (only with specific subheaders). * * @arg handle the handle. * @arg stream the stream to parse from. * @arg raw_type the raw file type. * @arg groupname the groupname (up to 16 bytes to read). * @arg dirname the directory name (up to 8 bytes are read). * @arg filename the filename (up to 8 bytes are read). * @arg filesize the data length. * @return the error code (0 if ok). */ int g1m_parse_mcsfile_content(g1m_mcsfile_t **handle, FILE *stream, int raw_type, const unsigned char *groupname, const unsigned char *dirname, const unsigned char *filename, uint_fast32_t filesize) { /* create handle */ *handle = malloc(sizeof(g1m_mcsfile_t)); if (!handle) return (g1m_error_alloc); /* save data */ g1m_mcsfile_t *h = *handle; h->type = 0; strncpy(h->name, (char*)filename, 8); /* copy raw data */ h->_group[16] = 0; strncpy(h->_group, (char*)groupname, 16); h->_dirname[8] = 0; strncpy(h->_dirname, (char*)dirname, 8); h->name[8] = 0; strncpy(h->name, (char*)filename, 8); /* look for the raw type */ unsigned int type; if (g1m_mcstype_getlib(h->_group, h->name, raw_type, &type, &h->id)) goto notparsing; log_info("libg1m file type is 0x%02x", type); if (g1m_mcstype_uses_id(type)) { log_info("libg1m file id is (%d, %d)", g1m_get_id_major(h->id), g1m_get_id_minor(h->id)); } /* look for the parsing function */ mcs_parse_func_t parse = lookup_mcsfile_parse(type); if (!parse) { log_error("No dedicated parsing function for this type was found!"); goto notparsing; } /* read it */ h->type = type; strncpy(h->name, (char*)filename, 8); int err = (*parse)(h, stream, filesize); /* free if error, and return */ if (err) { free(*handle); *handle = NULL; } return (err); notparsing:; /* read the content */ uint8_t buf[filesize]; READ(buf, filesize) /* save the content */ h->content = malloc(filesize); if (!h->content) return (g1m_error_alloc); memcpy(h->content, buf, filesize); h->content_size = filesize; /* log */ log_info("File content:"); logm_info(h->content, h->content_size); /* saved normally */ return (0); } /** * g1m_parse_mcs: * Parse an MCS file, after the Standard Header. * * @arg handle the handle. * @arg stream the stream to parse from. * @arg num number of sizes. * @return the error code (0 if ok). */ int g1m_parse_mcs(g1m_t *handle, FILE *stream, struct standard_header *std) { int err = g1m_error_alloc; /* get number of subparts from the standard header */ uint_fast16_t num = std->number; /* allocate memory for the files */ handle->count = 0; handle->_size = num; if (num) { handle->files = malloc(sizeof(g1m_mcsfile_t*) * num); if (!handle->files) return (g1m_error_alloc); } /* read all of the parts */ log_info("%" PRIuFAST16 " total mcs files to browse", num); while (handle->count < (int)num) { /* get the subheader */ GDREAD(hd, mcs_subheader) /* correct endianess */ hd.subcount = be32toh(hd.subcount); /* log info about part */ log_info("New group! Group name is '%.16s'.", hd.intname); log_info("%d mcs files to browse in that group", hd.subcount); /* foreach subpart */ for (uint_fast32_t i = 0; i < hd.subcount; i++) { /* get the part header */ GDREAD(fhd, mcs_fileheader) /* correct endianess */ fhd.datalength = be32toh(fhd.datalength); /* log info about the subpart */ log_info("[%" PRIuFAST32 "] directory name is '%.8s'", i, fhd.dirname); log_info("[%" PRIuFAST32 "] filename is '%.8s'", i, fhd.filename); log_info("[%" PRIuFAST32 "] data length is %" PRIu32, i, fhd.datalength); /* decode */ handle->files[handle->count] = NULL; err = g1m_parse_mcsfile_content(&handle->files[handle->count], stream, fhd.filetype, hd.intname, fhd.dirname, fhd.filename, fhd.datalength); if (err) return (err); handle->count++; } } /* no error */ return (0); /* was error! */ fail: g1m_free_mcs(handle); return (err); }