cake
/
libg1m
Archived
1
0
Fork 0
This repository has been archived on 2024-03-16. You can view files and clone it, but cannot push or open issues or pull requests.
libg1m/src/decode/casemul.c

427 lines
12 KiB
C

/* *****************************************************************************
* decode/casemul.c -- decode a Casemul file.
* Copyright (C) 2017 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr>
*
* This file is part of libg1m.
* libg1m is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3.0 of the License,
* or (at your option) any later version.
*
* libg1m is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libg1m; if not, see <http://www.gnu.org/licenses/>.
*
* Based on the Casetta project documentation:
* https://casetta.tuxfamily.org/formats/casemul
*
* The support for the subfiles (program, picture, matrix, list) are not added
* in the specific files in `decode/mcs/` as this is *not* the format they
* have while in the MCS, but the format CasEmul uses internally.
* ************************************************************************** */
#include <libg1m/internals.h>
#define e32toh(N) ((big_endian) ? be32toh(N) : le32toh(N))
#define htoe32(N) ((big_endian) ? htobe32(N) : htole32(N))
#define VER htoe32(0x00000100) /* 1.00 */
/* ************************************************************************** */
/* Utilities */
/* ************************************************************************** */
/**
* read_internal:
* Read the internal headers, and check them.
*
* @arg buffer the buffer to read from.
* @arg type the expected type.
* @arg version the expected version.
* @return the error (if any).
*/
static int read_internal(g1m_buffer_t *buffer, uint32_t signature,
uint_fast32_t version)
{
DREAD(hd, casemul_internal_header)
/* check type */
if (signature != hd.signature) {
const char *a = (char*)&signature, *b = (char*)&hd.signature;
log_error("signature mismatch!");
log_error("expected '%c%c%c%c', got '%c%c%c%c'",
a[0], a[1], a[2], a[3], b[0], b[1], b[2], b[3]);
return (g1m_error_magic);
}
/* check version */
if (le32toh(hd.version) != version) {
log_error("version mismatch!");
log_error("epxected %08" PRIxFAST32 ", got %08" PRIx32,
version, hd.version);
return (g1m_error_magic);
}
/* nothing wrong :) */
return (0);
}
/**
* read_top:
* Read the top of a record.
*
* @arg buffer the buffer to read from.
* @arg name the name buffer (13 bytes including '\0').
* @arg length the subheader+data length.
* @return the error code.
*/
static int read_top(g1m_buffer_t *buffer, char *name, uint_fast32_t *length)
{
/* read and check the name length */
uint32_t name_length; READ(&name_length, sizeof(uint32_t))
name_length = le32toh(name_length);
/* read the name and copy */
uint8_t buf[name_length];
READ(buf, name_length)
name_length = max(name_length, 8);
memcpy(name, buf, name_length);
name[name_length] = 0;
/* read the length and correct */
READ(length, sizeof(uint32_t))
*length = le32toh(*length);
/* no error! */
return (0);
}
/* ************************************************************************** */
/* Intermediate functions */
/* ************************************************************************** */
/**
* read_picture:
* Read a picture record and content.
*
* @arg pfile the pointer to the mcsfile to make and fill.
* @arg buffer the buffer to read from.
* @arg big_endian whether the file is big endian or not.
* @return the error (if occured).
*/
static int read_picture(g1m_mcsfile_t **pfile, g1m_buffer_t *buffer,
int big_endian)
{
int err; *pfile = NULL;
static const uint32_t colours[256] = {
[0x00] = 0xFFFFFF, /* white */
[0x01] = 0xFF8000, /* orange */
[0x02] = 0x00FF00, /* green */
[0x03] = 0x0000FF, /* blue */
/* other colours are black */
};
/* general record things */
char name[13]; uint_fast32_t record_length;
if ((err = read_top(buffer, name, &record_length))
|| (err = read_internal(buffer, MAKELONG('PI', 'CT'), VER)))
return (err);
/* specific things */
DREAD(pct, casemul_pict_header)
log_info("picture dimension is %d*%dpx", pct.width, pct.height);
/* read all of the pixels */
unsigned int total = pct.width * pct.height;
uint8_t rawpx[total]; READ(rawpx, total)
uint8_t *px = rawpx;
/* allocate the file */
g1m_mcsfile_t *file = malloc(sizeof(g1m_mcsfile_t));
if (!file) return (g1m_error_alloc);
memset(file, 0, sizeof(g1m_mcsfile_t));
/* make the pixels */
int width = pct.width, height = pct.height;
uint32_t **img = alloc_pixels(width, height);
if (!img) { free(file); return (g1m_error_alloc); }
/* set the head */
file->head.type = g1m_mcstype_capt;
memcpy(file->head.name, name, strlen(name) + 1);
/* TODO: id */
/* set the pixels */
prepare_pixels(img, width, height)
file->width = width;
file->height = height;
file->image = img;
for (int x = 0; x < width; x++) for (int y = 0; y < height; y++)
img[y][x] = colours[*px++];
/* finish */
*pfile = file;
return (0);
}
/**
* read_matrix:
* Read a matrix record and content.
*
* @arg pfile the pointer to the mcsfile to make and fill.
* @arg buffer the buffer to read from.
* @arg big_endian whether the file is big endian encoded or not.
* @return the error that occured.
*/
static int read_matrix(g1m_mcsfile_t **pfile, g1m_buffer_t *buffer,
int big_endian)
{
int err;
/* general record things */
char name[13]; uint_fast32_t record_length;
if ((err = read_top(buffer, name, &record_length))
|| (err = read_internal(buffer, MAKELONG('MT', 'RX'), VER)))
return (err);
/* read specific things */
DREAD(mtx, casemul_mtrx_header)
int width = le32toh(mtx.lines) & 0x7FFF,
height = le32toh(mtx.columns) & 0x7FFF;
log_info("%d*%d elements in matrix", width, height);
/* read double tab */
unsigned int total = width * height;
double tab[total]; READ(tab, total * sizeof(double))
double *raw = tab;
/* allocate handle */
g1m_mcsfile_t *file = malloc(sizeof(g1m_mcsfile_t));
if (!file) return (g1m_error_alloc);
memset(file, 0, sizeof(g1m_mcsfile_t));
/* allocate matrix */
file->cells = malloc(sizeof(g1m_mcs_cell_t*) * height);
if (!file->cells) { free(file); return (g1m_error_alloc); }
g1m_mcs_cell_t *rws = malloc(sizeof(g1m_mcs_cell_t) * height * width);
if (!rws) { free(file); free(file->cells); return (g1m_error_alloc); }
/* set the information */
file->head.type = g1m_mcstype_mat;
memcpy(file->head.name, name, strlen(name) + 1);
/* TODO: id */
/* read the matrix */
file->columns = width;
file->rows = height;
for (int y = 0; y < height; y++) {
/* correct offset in matrix */
file->cells[y] = &rws[width * y];
/* read column */
for (int x = 0; x < width; x++) {
/* read the bcd */
g1m_bcd_t bcd; g1m_bcd_fromdouble(*raw++, &bcd);
#if LOGLEVEL <= ll_info
/* log the bcd */
char buf[G1M_BCD_GOODBUFSIZE];
g1m_bcdtoa(&bcd, buf, G1M_BCD_GOODBUFSIZE);
log_info("[%d][%d] %s", y, x, buf);
#endif
/* make the cell */
file->cells[y][x] = (g1m_mcs_cell_t){
.real = bcd,
.imgn = {},
.used = 1
};
}
}
/* no error */
*pfile = file;
return (0);
}
/**
* read_list:
* Read a list record and content.
*
* @arg pfile the pointer to the mcsfile to make and fill.
* @arg buffer the buffer to read from.
* @arg big_endian whether the file is big endian encoded or not.
* @return the error that occured.
*/
static int read_list(g1m_mcsfile_t **pfile, g1m_buffer_t *buffer,
int big_endian)
{
int err;
/* general record things */
char name[13]; uint_fast32_t record_length;
if ((err = read_top(buffer, name, &record_length))
|| (err = read_internal(buffer, MAKELONG('LI', 'ST'), VER)))
return (err);
/* read specific things */
DREAD(lst, casemul_list_header)
int len = le32toh(lst.lines) & 0x7FFF;
log_info("%d elements in list", len);
/* read double tab */
double tab[len]; READ(tab, len * sizeof(double))
double *raw = tab;
/* allocate handle */
g1m_mcsfile_t *file = malloc(sizeof(g1m_mcsfile_t));
if (!file) return (g1m_error_alloc);
memset(file, 0, sizeof(g1m_mcsfile_t));
/* allocate matrix */
file->cells = malloc(sizeof(g1m_mcs_cell_t*) * len);
if (!file->cells) { free(file); return (g1m_error_alloc); }
g1m_mcs_cell_t *rws = malloc(sizeof(g1m_mcs_cell_t) * len);
if (!rws) { free(file->cells); free(file); return (g1m_error_alloc); }
/* set the information */
file->head.type = g1m_mcstype_list;
memcpy(file->head.name, name, strlen(name) + 1);
/* TODO: id */
/* read the list */
file->columns = 1; file->rows = len;
for (int x = 0; x < len; x++) {
/* correct matrix, read bcd */
file->cells[x] = &rws[x];
g1m_bcd_t bcd; g1m_bcd_fromdouble(*raw++, &bcd);
#if LOGLEVEL <= ll_info
/* log information */
char buf[G1M_BCD_GOODBUFSIZE];
g1m_bcdtoa(&bcd, buf, G1M_BCD_GOODBUFSIZE);
log_info("[%d] %s", x, buf);
#endif
/* set the cell */
file->cells[x][0] = (g1m_mcs_cell_t){
.real = bcd,
.imgn = {},
.used = 1
};
}
/* no error */
*pfile = file;
return (0);
}
/* ************************************************************************** */
/* Main function */
/* ************************************************************************** */
/**
* g1m_decode_casemul:
* Decode a CasEmul file.
*
* @arg handle the handle.
* @arg buffer the buffer to read from.
* @return the error code (0 if ok).
*/
int g1m_decode_casemul(g1m_t *handle, g1m_buffer_t *buffer)
{
int err, big_endian = handle->platform & g1m_platflag_be;
/* read the overall header's internal header end */
struct casemul_internal_header overall_int = {
.signature = MAKELONG('CA', 'FS')};
READ(&overall_int.version, 8);
if (e32toh(overall_int.version) != 0x100)
return (g1m_error_magic);
/* read the overall (global) header */
DREAD(glb, casemul_header)
#if LOGLEVEL <= ll_info
glb.source_offset = e32toh(glb.source_offset);
glb.compiled_offset = e32toh(glb.compiled_offset);
log_info("Header source offset is: 0x%08X", glb.source_offset);
if (glb.flags & casemul_compiled)
log_info("Header compiled offset is: 0x%08X", glb.compiled_offset);
else
log_info("The file has got no compiled part.");
#endif
/* read the source header */
if ((err = read_internal(buffer, MAKELONG('SR', 'CE'), VER)))
return (err);
DREAD(src, casemul_source_header)
/* prepare the tab */
handle->type = g1m_type_mcs;
handle->platform = g1m_platform_fx;
handle->count = 0;
handle->_size = /* src.programs + */ src.pictures + src.matrixes +
src.lists;
if (!(handle->files = malloc(sizeof(g1m_mcsfile_t*) * handle->_size)))
return (g1m_error_alloc);
/* read each program; TODO: put this in a function when token parsing
* is managed. */
for (int i = 0; i < src.programs; i++) {
log_info("Reading program #%d", i + 1);
log_warn("Program content will be skipped!");
/* general record things */
char name[13]; uint_fast32_t record_length;
if ((err = read_top(buffer, name, &record_length))
|| (err = read_internal(buffer, MAKELONG('PR', 'OG'), VER)))
goto fail;
/* specific things */
GDREAD(prg, casemul_prog_header)
prg.length = le32toh(prg.length);
/* TODO: decode using tokens (cf. refc and libfc) */
SKIP(prg.length)
}
/* read each picture */
for (int i = 0; i < src.pictures; i++) {
log_info("Reading picture #%d", i + 1);
if ((err = read_picture(&handle->files[handle->count], buffer,
big_endian)))
goto fail;
handle->count++;
}
/* read each matrix */
for (int i = 0; i < src.matrixes; i++) {
log_info("Reading matrix #%d", i + 1);
if ((err = read_matrix(&handle->files[handle->count], buffer,
big_endian)))
goto fail;
handle->count++;
}
/* read each list */
for (int i = 0; i < src.lists; i++) {
log_info("Reading list #%d", i + 1);
if ((err = read_list(&handle->files[handle->count], buffer,
big_endian)))
goto fail;
handle->count++;
}
/* TODO: skip compiled part? */
log_warn("Should read compiled part here!");
/* no error! */
return (0);
fail:
g1m_free_mcs(handle);
return (err);
}