cake
/
libg1m
Archived
1
0
Fork 0

Added Casemul files support.

This commit is contained in:
Thomas Touhey 2017-03-02 01:09:07 +01:00
parent 1b99b96f54
commit 931e14f1fd
8 changed files with 427 additions and 42 deletions

View File

@ -129,6 +129,9 @@ struct casemul_comp_header {
* uint32_t length;
* The subheader+data length.
*
* Actually, it looks like the name length is always 12 and finishes with
* "W:g\xA7"; I don't know why, I'll have to investiguate on this. XXX
*
* Then expect an internal header, part of the subheader, that expresses the
* type of it (expected version for all of them is 1.00). */
/* ************************************************************************** */
@ -150,7 +153,7 @@ struct casemul_prog_header {
struct casemul_pict_header {
/* size */
uint8_t w, h;
uint8_t width, height;
/* ... aaaaand alignment. (you know, unchecked theory?) */
uint8_t _align[2];

View File

@ -130,6 +130,7 @@ int g1m_decode_std(g1m_t *handle, const char *path, g1m_buffer_t *buffer,
struct standard_header*, g1m_type_t expected_types);
int g1m_decode_cas(g1m_t *handle, g1m_buffer_t *buffer,
g1m_type_t expected_types);
int g1m_decode_casemul(g1m_t *handle, g1m_buffer_t *buffer);
/* ************************************************************************** */
/* "Std"-specific decoding functions */

View File

@ -32,12 +32,18 @@
# include <libkern/OSByteOrder.h>
# define be16toh(x) OSSwapBigToHostInt16(x)
# define le16toh(x) OSSwapLittleToHostInt16(x)
# define be32toh(x) OSSwapBigToHostInt32(x)
# define le32toh(x) OSSwapLittleToHostInt32(x)
# define be64toh(x) OSSwapBigToHostInt64(x)
# define le64toh(x) OSSwapLittleToHostInt64(x)
# define htobe16(x) OSSwapHostToBigInt16(x)
# define htole16(x) OSSwapHostToLittleInt16(x)
# define htobe32(x) OSSwapHostToBigInt32(x)
# define htole32(x) OSSwapHostToLittleInt32(x)
# define htobe64(x) OSSwapHostToBigInt64(x)
# define htole64(x) OSSwapHostToLittleInt64(x)
# elif defined(__OpenBSD__)
# include <sys/endian.h>
@ -48,20 +54,32 @@
# if BYTE_ORDER == LITTLE_ENDIAN
# define be16toh(x) ntohs(x)
# define le16toh(x) (x)
# define be32toh(x) ntohl(x)
# define le32toh(x) (x)
# define be64toh(x) ntohll(x)
# define le64toh(x) (x)
# define htobe16(x) htons(x)
# define htole16(x) (x)
# define htobe32(x) htonl(x)
# define htole32(x) (x)
# define htobe64(x) htonll(x)
# define htole64(x) (x)
# else
# define be16toh(x) (x)
# define le16toh(x) ntohs(x)
# define be32toh(x) (x)
# define le32toh(x) ntohl(x)
# define be64toh(x) (x)
# define le64toh(x) ntohll(x)
# define htobe16(x) (x)
# define htole16(x) htons(x)
# define htobe32(x) (x)
# define htole32(x) htonl(x)
# define htobe64(x) (x)
# define htole64(x) htonll(x)
# endif
# else

View File

@ -29,23 +29,25 @@
/* MCS file type */
typedef unsigned int g1m_mcsfile_type_t;
typedef unsigned int g1m_mcstype_t;
# define g1m_mcstype_unknown 0x0000
# define g1m_mcstype_program 0x0001
# define g1m_mcstype_list 0x0002
# define g1m_mcstype_mat 0x0004
# define g1m_mcstype_pict 0x0008
# define g1m_mcstype_capt 0x0010
# define g1m_mcstype_spreadsheet 0x0020
# define g1m_mcstype_string 0x0040
# define g1m_mcstype_setup 0x0080
# define g1m_mcstype_alphamem 0x0100
# define g1m_mcstype_vct 0x0200
# define g1m_mcstype_variable 0x0400
# define g1m_mcstype_unknown 0x0000
# define g1m_mcstype_program 0x0001
# define g1m_mcstype_list 0x0002
# define g1m_mcstype_mat 0x0004
# define g1m_mcstype_pict 0x0008
# define g1m_mcstype_capt 0x0010
# define g1m_mcstype_ssheet 0x0020
# define g1m_mcstype_string 0x0040
# define g1m_mcstype_setup 0x0080
# define g1m_mcstype_alphamem 0x0100
# define g1m_mcstype_vct 0x0200
# define g1m_mcstype_variable 0x0400
/* MCS file type aliases */
# define g1m_mcstype_picture g1m_mcstype_pict
# define g1m_mcstype_capture g1m_mcstype_capt
# define g1m_mcstype_vector g1m_mcstype_vct
# define g1m_mcstype_matrix g1m_mcstype_mat
# define g1m_mcstype_picture g1m_mcstype_pict
# define g1m_mcstype_capture g1m_mcstype_capt
# define g1m_mcstype_spreadsheet g1m_mcstype_ssheet
# define g1m_mcstype_vector g1m_mcstype_vct
/* Macros to check if the type uses the ID, and to interact with it */
# define g1m_mcstype_uses_id(T) ((T) & (\

View File

@ -42,7 +42,7 @@ void g1m_bcd_fromdouble(double dbl, g1m_bcd_t *bcd)
exp++;
dbl /= 10;
}
while (dbl < 1) {
if (dbl > 0) while (dbl < 1) {
exp--;
dbl *= 10;
}

View File

@ -43,8 +43,15 @@ int g1m_decode(g1m_t *handle, const char *path, g1m_buffer_t *buffer,
if (buf[0] == ':')
return (g1m_decode_cas(handle, buffer, expected_types));
/* identify a Casemul file */
READ(&buf[1], 3)
log_info("Buffer content is:");
logm_info(buf, 4);
if (!memcmp(buf, "ACFS", 4))
return (g1m_decode_casemul(handle, buffer));
/* identify a standard header (send a _copy_) */
READ(&buf[1], 0x1F)
READ(&buf[1], 0x1C)
uint8_t altbuf[0x20]; memcpy(altbuf, buf, 0x20);
return (g1m_decode_std(handle, path, buffer,
(struct standard_header*)altbuf, expected_types));

378
src/decode/casemul.c Normal file
View File

@ -0,0 +1,378 @@
/* *****************************************************************************
* 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 VER 0x00000100 /* 1.00, the expected version in the internal headers */
/* ************************************************************************** */
/* 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, const char *type,
uint_fast32_t version)
{
DREAD(hd, casemul_internal_header)
/* check type */
if (type[0] != hd.type[1] || type[1] != hd.type[0]
|| type[2] != hd.type[3] || type[3] != hd.type[2]) {
log_error("type mismatch!");
log_error("expected '%c%c%c%c', got '%c%c%c%c'",
type[0], type[1], type[2], type[3],
hd.type[1], hd.type[0], hd.type[3], hd.type[2]);
return (g1m_error_magic);
}
/* check version */
if (le32toh(hd.version) != version) {
log_error("version mismatch!");
log_error("version mismatch!");
log_error("epxected %08" PRIxFAST32 ", got %08" PRIx32,
version, le32toh(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;
logm_info(name, name_length);
/* 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.
* @return the error (if occured).
*/
static int read_picture(g1m_mcsfile_t **pfile, g1m_buffer_t *buffer)
{
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, "PICT", VER)))
return (err);
/* specific things */
DREAD(pct, casemul_pict_header)
/* 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.
* @return the error that occured.
*/
static int read_matrix(g1m_mcsfile_t **pfile, g1m_buffer_t *buffer)
{
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, "MTRX", VER)))
return (err);
/* read specific things */
DREAD(mtx, casemul_mtrx_header)
/* read double tab */
int width = le32toh(mtx.lines) & 0x7FFF,
height = le32toh(mtx.columns) & 0x7FFF;
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++) {
file->cells[y] = &rws[width * y];
for (int x = 0; x < width; x++) {
g1m_bcd_t bcd; g1m_bcd_fromdouble(*raw++, &bcd);
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.
* @return the error that occured.
*/
static int read_list(g1m_mcsfile_t **pfile, g1m_buffer_t *buffer)
{
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, "LIST", VER)))
return (err);
/* read specific things */
DREAD(lst, casemul_list_header)
/* read double tab */
int len = le32toh(lst.lines) & 0x7FFF;
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++) {
file->cells[x] = &rws[x];
g1m_bcd_t bcd; g1m_bcd_fromdouble(*raw++, &bcd);
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;
/* read the overall header's internal header end */
struct casemul_internal_header overall_int = {.type = "ACFS"};
READ(&overall_int.version, 8);
if (le32toh(overall_int.version) != 0x100)
return (g1m_error_magic);
/* read the overall (global) header */
DREAD(glb, casemul_header)
/* read the source header */
if ((err = read_internal(buffer, "SRCE", VER)))
return (err);
DREAD(src, casemul_source_header)
log_info("%d program(s), %d pic(s), %d matrix(es), %d list(s)",
src.programs, src.pictures, src.matrixes, src.lists);
/* 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++) {
/* general record things */
char name[13]; uint_fast32_t record_length;
if ((err = read_top(buffer, name, &record_length))
|| (err = read_internal(buffer, "PROG", 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++) {
if ((err = read_picture(&handle->files[handle->count], buffer)))
goto fail;
handle->count++;
}
/* read each matrix */
for (int i = 0; i < src.matrixes; i++) {
if ((err = read_matrix(&handle->files[handle->count], buffer)))
goto fail;
handle->count++;
}
/* read each list */
for (int i = 0; i < src.lists; i++) {
if ((err = read_list(&handle->files[handle->count], buffer)))
goto fail;
handle->count++;
}
/* TODO: skip compiled part? */
/* no error! */
return (0);
fail:
g1m_free_mcs(handle);
return (err);
}

View File

@ -1,24 +0,0 @@
/* *****************************************************************************
* 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
* ************************************************************************** */
#include <libg1m/internals.h>
/* TODO */