cake
/
libg1m
Archived
1
0
Fork 0

Modified main handle MCS management, started adding G3P support

This commit is contained in:
Thomas Touhey 2016-11-23 06:10:45 +01:00
parent 9a09e71e08
commit 34442dc0ae
9 changed files with 239 additions and 83 deletions

View File

@ -61,7 +61,7 @@
# Linker
LD := gcc
# - Linker flags
LDFLAGS := -shared -lusb-1.0 \
LDFLAGS := -shared -lz \
-e __lib$(NAME)_version \
-Wl,-soname,lib$(NAME).so.$(MAJOR) \
-Wl,-z,relro -Wl,-z,combreloc -Wl,-z,defs

View File

@ -0,0 +1,50 @@
# libg1m - Casio File Format manipulation
## Introduction
All of the files created around CASIO calculators are declinations of one
format, which I named by the name of one of them : G1M. These formats go
from main memory saves to pictures and e-activities (documents).
This library aims to be able to parse and write in all of the available
formats, and to centralize the documentation and work on them, in order
to provide a simple interface for C programs, that can be adapted to
provide other interfaces such as a Python one, and to hide the odd
subtilities of this Patchwork As A Format.
## Prerequisites
Side note : the library might work with older versions of these dependencies,
I took these as a reference because these are the ones I work with.
### Making-only dependencies
| Name | Version |
| -------------------------------------------------- | -------- |
| [make](https://www.gnu.org/software/make/) | >= 4.0 |
| [gcc](https://gcc.gnu.org/) | >= 4.9 |
| [binutils](https://www.gnu.org/software/binutils/) | >= 2.25 |
| [asciidoc](http://asciidoc.org/) | >= 8.6.9 |
| [gzip](https://www.gnu.org/software/gzip/) | >= 1.6 |
### Runtime dependencies
| Name | Version |
| -------------------------------------------------- | -------- |
| [zlib](http://zlib.net/) | >= 1.2.8 |
## Building
Just `./configure` then `make`.
To install, use `make install`.
To build and install only the lib, use `all-lib` then `install-lib`.
To build and install only the docs, use `all-doc` and `install-doc`.
Other useful targets:
- `uninstall`, `uninstall-lib`, `uninstall-bin`, `uninstall-doc`:
will try to uninstall using the current configuration (experimental);
- `mostlyclean`, `clean`, `clean-doc`, `mostlyclean-lib`, `clean-lib`:
remove built files at different levels;
- `re`, `re-lib`, `re-doc`: regenerate built files at different levels
(clean and build) -- useful when configuration is changed.

View File

@ -137,9 +137,9 @@ typedef struct {
time_t creation_date;
/* MCS RELATED DATA */
int part_count;
int _parts_size; /* used internally for reallocs */
g1m_mcs_t **parts;
int mcs_count;
int _mcs_size; /* used internally for reallocs */
g1m_mcs_t **mcs;
/* TODO: pictures, e-activities */
} g1m_t;

View File

@ -32,11 +32,13 @@ enum g1m_type {
/* e-activity (document) */
g1m_typ_eact = 0x49,
/* g1r equivalent - different to g1m because of OS 2?
* (quick way to check expected compatibility? NOT RELIABLE) */
/* g1r equivalent */
g1m_typ_g2r = 0x62,
/* g3p (fx-CGxx picture) */
/* g3m (fx-CG program) */
g1m_typ_g3m = 0x75,
/* g3p (fx-CG picture) */
g1m_typ_g3p = 0x7d,
/* add-in (compiled program) */
@ -62,7 +64,7 @@ struct standard_header {
uint8_t type;
/* magic numbers, not always the same:
* - for g1m_type_addin_cg: {0x00, 0x01, 0x00, 0x01, 0x00}
* - for fx-CG formats: {0x00, 0x01, 0x00, 0x01, 0x00}
* - otherwise: {0x00, 0x10, 0x00, 0x10, 0x00} */
uint8_t magic[5];
@ -78,8 +80,11 @@ struct standard_header {
/* second control byte: filesize LSB + 0xb8 */
uint8_t control2;
/* 4-bytes alignment */
uint8_t align[9];
/* alignment */
uint8_t align[8];
/* is obfuscated - useful for G3P */
uint8_t obfuscated;
/* number of objects contained (useful for MCS filetype) */
uint16_t number;

View File

@ -12,6 +12,8 @@
# include <stdint.h>
/* These are pictures for fx-CG. They only have one part.
* This mysteries over this format have been unraveled by many, but I'm using
* Simon Lothar's and Kerm Martian's documentations.
*
* Some color depth things: */
@ -20,7 +22,7 @@ enum g3p_colorsize {
g3p_color_16bit = 0x10
};
/* The subheader is: */
/* So, after the header, here is the G3P subheader: */
struct g3p_subheader {
/* some magic sequence: "CP0100Ly755"
@ -28,42 +30,82 @@ struct g3p_subheader {
uint8_t magic[11];
/* unused */
uint8_t unused[11];
uint8_t unused[5];
/* plenty of unused space, or undocumented, for some reason */
uint8_t undocumented[130];
/* size of the file after standard header */
uint32_t g3p_size;
/* magic sequence? "0100" */
uint8_t magic2[4];
/* undocumented */
uint8_t unknown[4];
/* control sequence? <length of packed data + 4> */
uint32_t control;
/* size of the image data + 0x18 bytes of header */
uint32_t image_size;
/* undocumented, again */
uint16_t undocumented2;
/* undocumented gap */
uint8_t undocumented_gap[124];
};
/* dimensions */
/* What is after the G3P subheader is the image (data).
*
* The image has a header, some data and a footer. Here is the header: */
struct g3p_imageheader {
/* magic sequence? is: 0x00, 0x01, 0x00, 0x00 */
uint8_t magic[4];
/* size of the image data + footer */
uint32_t df_size;
/* w0t */
uint16_t unknown2;
/* width */
uint16_t width;
/* height */
uint16_t height;
/* color depth - see `g3p_colorsize` */
uint16_t color_depth;
/* undocumented, the third coming */
uint16_t undocumented3;
/* undocumented, again */
uint32_t undocumented;
/* length of the deflated image */
uint32_t dimage_length;
/* length of image + footer */
uint32_t data_size;
};
/* And after the image: */
/* Then there is the image data and the footer.
*
* At the beginning of the image data, there is a 2-byte ID.
* 0x3c1b is the ID for "Casio Provided" images with no footers (?).
*
* The image is deflated using the DEFLATE algorithm.
*
* For some images, CASIO added some obfuscation. To check if the image is
* encrypted, according to syscalls, you have to check standard header data.
* Image is encrypted if:
*
* [0x1C] != [0x08] + [0x12] + [0x13] + 0x7B
*
* Which, simplified and with our structures, is:
*
* std.obfuscated + 8 != (std.filesize & 0xff00) >> 8
*
* then the deflated image is encrypted.
*
* If this is the case, before passing the data to the INFLATE algorithm,
* you have to apply this on each byte of the deflated image:
*
* 0b76543210 -> 0b21076543,
* which, in C, is: (byte >> 3) | ((byte & 0x7) << 5)
*
* The footer contains the Adler32 checksum of the raw and unobfuscated data.
* Here it is: */
struct g3p_footer {
/* magic sequence: "0100" */
uint8_t magic[4];
/* some unused bytes? */
uint8_t unused[0x88];
struct g3p_imagefooter {
/* checksum */
uint32_t checksum;
};
# pragma pack()

View File

@ -24,8 +24,7 @@
int g1m_parse(g1m_t *handle, FILE *stream)
{
/* initialize the handle */
handle->type = 0x00;
handle->parts = NULL;
bzero(handle, sizeof(g1m_t));
/* get the standard header */
DREAD(hd, standard_header)

View File

@ -526,18 +526,15 @@ int g1m_parse_mcs(g1m_t *handle, FILE *stream, uint_fast16_t num)
* was the standard header. */
/* allocate memory for the subparts */
handle->part_count = 0;
handle->_parts_size = num;
handle->parts = malloc(sizeof(g1m_mcs_t*) * num);
if (!handle->parts) return (g1m_error_alloc);
bzero(handle->parts, sizeof(g1m_mcs_t*) * num);
/* proxy */
g1m_mcs_t **pmcs = handle->parts;
handle->mcs_count = 0;
handle->_mcs_size = num;
handle->mcs = malloc(sizeof(g1m_mcs_t*) * num);
if (!handle->mcs) return (g1m_error_alloc);
bzero(handle->mcs, sizeof(g1m_mcs_t*) * num);
/* read all of the parts */
log_info("%ld parts to browse", num);
for (uint_fast16_t i = 0; i < num; i++) {
log_info("%ld total mcs files to browse", num);
for (int i = 0; num; i++) {
/* get the subheader */
GDREAD(hd, mcs_subheader)
@ -545,17 +542,28 @@ int g1m_parse_mcs(g1m_t *handle, FILE *stream, uint_fast16_t num)
hd.subcount = be32toh(hd.subcount);
/* log info about part */
log_info("[%ld] Internal name is '%.16s'", i, hd.intname);
log_info("[%d] Internal name is '%.16s'", i, hd.intname);
/* check mcs tab length - in case of 0-files mcs */
if (i >= handle->_mcs_size) {
size_t old_mcs_size = handle->_mcs_size;
handle->_mcs_size += num; /* should contain this element & others */
g1m_mcs_t **nmcs = malloc(sizeof(g1m_mcs_t*) * handle->_mcs_size);
if (!nmcs) goto fail;
memcpy((void*)nmcs, (void*)handle->mcs, old_mcs_size);
handle->mcs = nmcs;
}
/* allocate tab and proxy */
err = g1m_error_alloc;
pmcs[i] = malloc(sizeof(g1m_mcs_t));
if (!pmcs[i]) goto fail;
g1m_mcs_t *mcs = pmcs[i];
handle->mcs[i] = malloc(sizeof(g1m_mcs_t));
if (!handle->mcs[i]) goto fail;
g1m_mcs_t *mcs = handle->mcs[i];
/* increment parts count, and remove to num the "extra" subparts */
handle->part_count++;
num -= hd.subcount - 1;
handle->mcs_count++;
num -= hd.subcount;
/* prepare it */
mcs->file_count = hd.subcount;
@ -566,8 +574,8 @@ int g1m_parse_mcs(g1m_t *handle, FILE *stream, uint_fast16_t num)
bzero(mcs->files, sizeof(g1m_mcsfile_t*) * hd.subcount);
/* foreach subpart */
log_info("[%ld] %d mcs files to browse", i, hd.subcount);
for (uint_fast32_t j = 0; j < hd.subcount; j++) {
log_info("[%d] %d mcs files to browse", i, hd.subcount);
for (int j = 0; j < (int)hd.subcount; j++) {
/* get the part header */
GDREAD(fhd, mcs_fileheader)
@ -575,11 +583,11 @@ int g1m_parse_mcs(g1m_t *handle, FILE *stream, uint_fast16_t num)
fhd.datalength = be32toh(fhd.datalength);
/* log info about the subpart */
log_info("[%ld,%ld] directory name is '%.8s'", i, j, fhd.dirname);
log_info("[%ld,%ld] filename is '%.8s'", i, j, fhd.filename);
log_info("[%ld,%ld] file type is '%s' (0x%02d)",
log_info("[%d,%d] directory name is '%.8s'", i, j, fhd.dirname);
log_info("[%d,%d] filename is '%.8s'", i, j, fhd.filename);
log_info("[%d,%d] file type is '%s' (0x%02d)",
i, j, g1m_get_mcs_ftype_string(fhd.filetype), fhd.filetype);
log_info("[%ld,%ld] data length is %u", i, j, fhd.datalength);
log_info("[%d,%d] data length is %u", i, j, fhd.datalength);
/* decode */
int err = g1m_parse_mcsfile_content(&mcs->files[j], stream,

View File

@ -8,6 +8,23 @@
/* */
/* ************************************************************************** */
#include <libg1m/internals.h>
#include <zlib.h>
/**
* g3p_deobfuscate:
* De-obfuscate the image data.
*
* @arg buf the buffer.
* @arg n the buffer size.
*/
static void g3p_deobfuscate(uint8_t *buf, size_t n)
{
while (n--) {
int byte = *buf;
*buf++ = (byte >> 3) | ((byte & 0x7) << 5);
}
}
/**
* g1m_parse_g3p:
@ -22,39 +39,74 @@
int g1m_parse_g3p(g1m_t *handle, FILE *stream,
struct standard_header *std)
{
/* get the header and footer */
/* get the G3P global header */
DREAD(hd, g3p_subheader)
/* correct endianess */
hd.control = be32toh(hd.control);
hd.width = be16toh(hd.width);
hd.height = be16toh(hd.height);
hd.color_depth = be16toh(hd.color_depth);
hd.dimage_length = be32toh(hd.dimage_length);
/* check control */
/* check magic */
if (memcmp(hd.magic, "CP", 2)) {
log_info("could not read CP.");
return (g1m_error_magic);
} else if (memcmp(hd.magic, "0100", 4)) {
log_info("could not read header 0100");
log_fatal("could not read 'CP'.");
return (g1m_error_magic);
}
/* TODO: what about "length of packed data + 4" control? */
/* TODO: get footer, check control 0100 */
/* read the image header */
DREAD(ihd, g3p_imageheader)
/* check if image is encrypted */
int is_encrypted = (((std->filesize & 0xff) +
((std->filesize & 0xff00) >> 8) + g1m_typ_g3p) & 0xff) ^ 0x1c;
/* correct endianness */
ihd.df_size = be32toh(ihd.df_size);
ihd.width = be16toh(ihd.width);
ihd.height = be16toh(ihd.height);
ihd.color_depth = be16toh(ihd.color_depth);
ihd.data_size = be32toh(ihd.data_size);
/* check some little things */
int is_obfuscated = ((uint8_t)(std->obfuscated + 8)
!= (uint8_t)((std->filesize & 0xff00) >> 8));
int has_footer = 1; /* might change? */
size_t deflated_image_size = ihd.data_size - has_footer * 0x4;
/* log info */
log_info("Width: %dpx, height: %dpx", hd.width, hd.height);
log_info("Width: %dpx, height: %dpx", ihd.width, ihd.height);
log_info("Pixel depth: %s",
(hd.color_depth == g3p_color_4bit) ? "4-bit" : "16-bit");
log_info("Deflated image length: %uo", hd.dimage_length);
log_info("Is encrypted: %s", is_encrypted ? "yes" : "no");
(ihd.color_depth == g3p_color_4bit) ? "4-bit" : "16-bit");
log_info("Deflated image length: %uo", ihd.data_size - 0x04);
log_info("Is obfuscated: %s", is_obfuscated ? "yes" : "no");
/* read image */
uint8_t deflated_image[deflated_image_size];
READ(deflated_image, deflated_image_size)
/* read footer and check sum */
if (has_footer) {
DREAD(ft, g3p_imagefooter);
ft.checksum = be32toh(ft.checksum);
if (adler32(0, deflated_image, deflated_image_size) != ft.checksum) {
log_fatal("Incorrect Adler32 checksum!");
return (g1m_error_magic);
}
}
/* unobfuscate if required */
if (is_obfuscated)
g3p_deobfuscate(deflated_image, deflated_image_size);
/* find out length of inflated data */
uLongf inflated_size = 0L; int z_err;
if ((z_err = uncompress(Z_NULL, &inflated_size, deflated_image,
deflated_image_size)) != Z_MEM_ERROR) {
log_fatal("Zlib error: error #%d", z_err);
return (g1m_error_magic);
}
log_info("Inflated image size is %zu", inflated_size);
/* allocate space for it, and uncompress */
uint8_t inflated_image[inflated_size];
if ((z_err = uncompress(inflated_image, &inflated_size, deflated_image,
deflated_image_size))) {
log_fatal("Zlib error: error #%d", z_err);
return (g1m_error_magic);
}
/* TODO: store pixels */
/* no error */
return (0);
}

View File

@ -48,12 +48,12 @@ void g1m_free_mcsfile(g1m_mcsfile_t *handle)
void g1m_free_mcs(g1m_t *handle)
{
/* check if mcs */
if (!handle->parts)
if (!handle->mcs)
return ;
/* foreach mcs */
g1m_mcs_t **mcs = handle->parts;
int mcs_count = handle->part_count;
g1m_mcs_t **mcs = handle->mcs;
int mcs_count = handle->mcs_count;
for (int i = 0; i < mcs_count; i++) {
/* check if used */
if (!mcs[i])
@ -69,7 +69,7 @@ void g1m_free_mcs(g1m_t *handle)
free(mcs[i]);
}
free(mcs);
handle->parts = NULL;
handle->mcs = NULL;
}
/**
@ -84,7 +84,7 @@ void g1m_free(g1m_t *handle)
if (!handle) return ;
/* mcs time! */
if (handle->type & g1m_type_mcs && handle->parts)
if (handle->type & g1m_type_mcs)
g1m_free_mcs(handle);
/* free the handle */