cake
/
libg1m
Archived
1
0
Fork 0

Reorganized into modules

This commit is contained in:
Thomas Touhey 2016-11-02 16:32:30 +01:00
parent 56c9c0dc82
commit 0a98f5b682
23 changed files with 1472 additions and 1313 deletions

View File

@ -77,33 +77,37 @@ reinstall: uninstall install
# Make the library.
all-lib: $(CHECKCFG) lib$(NAME).so
# Make the objects directory.
$(OBJDIR):
# Make a module object directory.
$(MODULES:%=$(OBJDIR)/%):
$(call bcmd,mkdir,$@,$(MD) $@)
# Make an object out of a source file.
$(OBJDIR)/%.o: $(SRCDIR)/%.c | $(OBJDIR)
$(call bcmd,cc,$@,$(CC) -c -o $@ $< $(CFLAGS))
# Make a module object out of a module source file.
define make-moduleobj-rule
$(OBJDIR)/$1/%.o: $(SRCDIR)/$1/%.c | $(OBJDIR)/$1
$(call bcmd,cc,$$@,$(CC) -c -o $$@ $$< $(CFLAGS))
endef
$(foreach mod,$(MODULES), \
$(eval $(call make-moduleobj-rule,$(mod))))
# Make the (shared) library.
lib$(NAME).so: $(SRC:%=$(OBJDIR)/%.o)
# Make the library (shared).
lib$(NAME).so: $(foreach m,$(MODULES),$(SRC_$(m):%=$(OBJDIR)/$(m)/%.o))
$(call bcmd,ld,$@,$(LD) -o $@ $^ $(LDFLAGS))
# Remove the objects directory.
mostlyclean-lib:
$(call rmsg,Removing the objects directory.)
$(call rmsg,Removing object directory.)
$(call rcmd,$(OBJDIR),$(RM) -r $(OBJDIR))
mclean-lib: mostlyclean-lib
# Clean and remove the built library
# Clean and remove the built library.
clean-lib: mclean-lib
$(call rmsg,Removing the built library.)
$(call rmsg,Removing the library.)
$(call rcmd,lib$(NAME).so,$(RM) lib$(NAME).so)
# Remake the library
# Remake the library.
re-lib: clean-lib all-lib
# Install the library and development files.
# Install the library, development files and udev rule.
install-lib: $(CHECKCFG) lib$(NAME).so
$(call imsg,Installing the library.)
$(call qcmd,$(INST) -m 755 -d "$(ILIBDIR)")
@ -122,10 +126,11 @@ install-lib: $(CHECKCFG) lib$(NAME).so
$(call qcmd,$(INST) -m 755 -d $(patsubst %,"$(IINCDIR)/%",\
$(sort $(dir $(INCPUB))))))
$(if $(INSTALL_DEVEL),\
$(foreach i,$(INCPUB),$(call icmd,$(i).h,\
$(INST) -m 644 $(INCDIR)/$(i).h "$(IINCDIR)/$(i).h"$(\n))))
$(foreach i,$(INCPUB),\
$(call icmd,$(i).h,\
$(INST) -m 644 $(INCDIR)/$(i).h "$(IINCDIR)/$(i).h"$(\n))))
# Uninstall the library and development files. (EXPERIMENTAL)
# Uninstall the library, development files and udev rule. (experimental)
uninstall-lib: $(CHECKCFG)
$(call rmsg,Uninstalling the library.)
$(call rcmd,lib$(NAME).a)

View File

@ -80,9 +80,19 @@
GZIP := gzip -f
#******************************************************************************#
# Look for sources #
# Look for modules and modules sources #
#******************************************************************************#
SRC := $(basename $(shell find $(SRCDIR) -name "*.c" -printf "%P\n"))
# Look for modules first
MODULES := $(notdir $(shell find $(SRCDIR) -mindepth 1 -maxdepth 1 \
-type d | sort))
# Then look for their content
define get-module-source
SRC_$1 := $(basename $(shell find $(SRCDIR)/$1 \
-maxdepth 1 -mindepth 1 -type f -name "*.[cs]" -printf "%P\n" | sort))
endef
$(foreach mod,$(MODULES), \
$(eval $(call get-module-source,$(mod))))
#******************************************************************************#
# Look for public headers (not internals.h or internals/**/*.h #

99
include/libg1m/format.h Normal file
View File

@ -0,0 +1,99 @@
/* ************************************************************************** */
/* _____ _ */
/* libg1m/format/main.h |_ _|__ _ _| |__ ___ _ _ */
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
/* | | (_) | |_| | | | | __/ |_| | */
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
/* Last updated: 2016/11/02 14:15:46 |___/ */
/* */
/* ************************************************************************** */
#ifndef LIBG1M_FORMAT_H
# define LIBG1M_FORMAT_H
# include <stdint.h>
# pragma pack(1)
/* Welcome on this new episode of Monster Circus! Today we'll present to you
* something that you might not be able to forget: the G1M format.
*
* This format is used by CASIO with its calculator for storing things
* like add-ins (compiled programs), MCS (main memory saves), and others.
* In fact, there is no name for the general format, only names for the
* "subformats" based on this one.
*
* Here's a complete list of the declinations and corresponding IDs: */
enum g1m_type {
/* fx-CG add-in */
g1m_type_addin_cg = 0x2c,
/* mcs (main memory) save */
g1m_type_mcs = 0x31,
/* e-activity (document) */
g1m_type_eact = 0x49,
/* g1r equivalent (no idea why it isn't 0x31 '-') */
g1m_type_g2r = 0x62,
/* g3p (fx-CGxx picture) */
g1m_type_g3p = 0x7d,
/* add-in (compiled program) */
g1m_type_addin = 0xf3,
};
/* It all starts with a header, called Standard Header by Simon Lothar.
* This Standard Header contains the total filesize, the G1M type (add-in,
* MCS, e-acts), some data that will be useful for the MCS type, and some
* magic and control bytes.
*
* Keep in mind that, everywhere in the header, multi-bytes integers
* are BIG ENDIAN.
*
* The LSB is the Least Significant Byte: once adapted to the host endianness,
* it can simply be obtained bitwise-AND-ing with 0xff. */
struct standard_header {
/* the file identifier, should be "USBPower". */
uint8_t main_id[8];
/* our filetype! */
uint8_t type;
/* magic numbers, not always the same:
* - for g1m_type_addin_cg: {0x00, 0x01, 0x00, 0x01, 0x00}
* - otherwise: {0x00, 0x10, 0x00, 0x10, 0x00} */
uint8_t magic[5];
/* first control byte: filesize LSB + 0x41 */
uint8_t control;
/* said to be 0x01, but doesn't need to */
uint8_t align_one;
/* total filesize */
uint32_t filesize;
/* second control byte: filesize LSB + 0xb8 */
uint8_t control2;
/* 4-bytes alignment */
uint8_t align[9];
/* number of objects contained (useful for MCS filetype) */
uint16_t number;
};
# pragma pack()
/* After the Standard Header is read and the G1M type is read, we have parts,
* each with their own subheaders and their own subtilities.
*
* Where do you want to start? */
# include <libg1m/format/addin.h>
# include <libg1m/format/addin_cg.h>
# include <libg1m/format/eact.h>
# include <libg1m/format/mcs.h>
# include <libg1m/format/picture_cg.h>
#endif /* LIBG1M_FORMAT_H */

View File

@ -0,0 +1,51 @@
/* ************************************************************************** */
/* _____ _ */
/* libg1m/format/addin.h |_ _|__ _ _| |__ ___ _ _ */
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
/* | | (_) | |_| | | | | __/ |_| | */
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
/* Last updated: 2016/11/02 14:15:46 |___/ */
/* */
/* ************************************************************************** */
#ifndef LIBG1M_FORMAT_ADDIN_H
# define LIBG1M_FORMAT_ADDIN_H
# include <stdint.h>
# pragma pack(1)
/* Add-ins are compiled programs. They only have one G1M part,
* These add-ins usually have the `g1a` extension.
*
* There is a declination of this format for fx-CG calculators:
* See `libg1m/format/addin_cg.h`.
*
* with one subheader, which is the following: */
struct g1a_subheader {
/* the internal name, of format "@APPNAME".
* useful for add-ins calling themselves... I guess? */
uint8_t internal_name[8];
/* the number of estrips (I don't know yet) */
uint8_t estrips_count;
/* the add-in version, of format "01.23.4567"
* the "01.23" will be displayed in SYSTEM > VERSION */
uint8_t version[10];
/* the add-in creation type, of format "YYYY.MMDD.HHMM" */
uint8_t creation_date[14];
/* 30x17 pixel menu icon bitmap */
uint8_t icon[68];
/* program title */
uint8_t title[8];
/* and the filesize of the part! */
uint32_t filesize;
};
/* Then the G1A file will just contain the add-in code and stop. */
# pragma pack()
#endif /* LIBG1M_FORMAT_ADDIN_H */

View File

@ -0,0 +1,102 @@
/* ************************************************************************** */
/* _____ _ */
/* libg1m/format/addin_cg.h |_ _|__ _ _| |__ ___ _ _ */
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
/* | | (_) | |_| | | | | __/ |_| | */
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
/* Last updated: 2016/11/02 14:15:46 |___/ */
/* */
/* ************************************************************************** */
#ifndef LIBG1M_FORMAT_ADDIN_CG_H
# define LIBG1M_FORMAT_ADDIN_CG_H
# include <stdint.h>
# pragma pack(1)
/* If you don't know what an add-in is, see `libg1m/format/addin.h`.
* This is the declination of the `G1A` format for fx-CG calculators.
*
* fx-CG add-ins also have one part only.
* This is the header: */
struct g3a_subheader {
/* byte checksum: 0x0000 to 0x001F (standard header)
* and 0x0024 to EOF (everything after this checksum), minus 4. */
uint32_t checksum;
/* magic sequence: {0x01, 0x01} */
uint8_t magic[2];
/* undocumented */
uint8_t undocumented[8];
/* control: filesize - 0x7000 - 4 */
uint32_t control;
/* undocumented */
uint8_t undocumented2[14];
/* title (zero terminated) */
uint8_t title[16];
/* undocumented */
uint8_t undocumented3[12];
/* filesize */
uint32_t filesize;
/* internal name (preceded by @, filled up with zeros */
uint8_t intname[11];
/* language labels */
uint8_t language_1_label[24];
uint8_t language_2_label[24];
uint8_t language_3_label[24];
uint8_t language_4_label[24];
uint8_t language_5_label[24];
uint8_t language_6_label[24];
uint8_t language_7_label[24];
uint8_t language_8_label[24];
/* eAct strip flag (0x00: cannot be used, 0x01: can be used) */
uint8_t eact_strip_flag;
/* some null bytes */
uint8_t zeroes[4];
/* version: "01.00.0000" then zeroes */
uint8_t version[12];
/* timestamp: "2012.0903.1652" */
uint8_t stamp[14];
/* null bytes, again */
uint8_t zeroes2[38];
/* eAct strip labels */
uint8_t eact_strip_label_1[36];
uint8_t eact_strip_label_2[36];
uint8_t eact_strip_label_3[36];
uint8_t eact_strip_label_4[36];
uint8_t eact_strip_label_5[36];
uint8_t eact_strip_label_6[36];
uint8_t eact_strip_label_7[36];
uint8_t eact_strip_label_8[36];
/* eAct icon */
uint8_t icon[0x300];
/* unused */
uint8_t unused[0x92c];
/* G3A filename (seriously?!) */
uint8_t g3a_filename[0x144];
/* selected and unselected icon image */
uint8_t selected_icon_image[0x3000];
uint8_t unselected_icon_image[0x3000];
};
/* Then the G3A file will contain the add-in code and stop. */
# pragma pack()
#endif

View File

@ -0,0 +1,125 @@
/* ************************************************************************** */
/* _____ _ */
/* libg1m/format/eact.h |_ _|__ _ _| |__ ___ _ _ */
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
/* | | (_) | |_| | | | | __/ |_| | */
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
/* Last updated: 2016/11/02 14:15:46 |___/ */
/* */
/* ************************************************************************** */
#ifndef LIBG1M_FORMAT_EACT_H
# define LIBG1M_FORMAT_EACT_H
# include <stdint.h>
/* E-Activities are the format CASIO uses for in-calc documents.
* It is the funniest subformat the the libg1m can parse.
*
* There is only one part for e-activities.
* The overall header has two zones:
* - the special header, that keeps the global info;
* - the setup area, that keeps one part of the calculator setup.
*
* There are several types for E-Activies, identified by the extensions
* CASIO give them.
* The E-Act version in the header is 0x10100 for g1e, 0x10200 for g2e,
* and 0x10400 for g3e. */
# define EACT_G1E 0x10100
# define EACT_G2E 0x10200
# define EACT_G3E 0x10400
/* The differences between these formats are the following:
* - On g1e, the OS version is always 0x5061636B, where on g2e/g3e,
* the OS version is the version of the OS on which the file was written.
* - On g1e/g2e, the setup directory is 0x10-bytes long, where on g3e,
* the setup directory is 0x2C-bytes long.
*
* So the main header is: */
struct eact_header {
/* the filesize */
uint32_t filesize;
/* the size of the setup area */
uint32_t setup_area_size;
/* the E-Act version */
uint32_t eact_version;
/* The OS version - only with G2E/G3E */
uint32_t os_version;
};
/* And now, the funniest part: the content.
* So a content has this header: */
struct eact_contentheader {
/* content type: @EACT, @RUNMAT, ... */
uint8_t type[8];
/* align for element name */
uint8_t align[8];
/* element name: "EACT1", "TEXT1", ... */
uint8_t name[16];
/* align for subheader */
uint8_t align2[4];
};
/* There are several types of contents.
* The most basic one is also called "@EACT".
* It has this content subheader: */
struct eact_eactheader {
/* line count */
uint32_t line_count;
};
/* And after, lines come. First of all, there is a line descriptor table,
* that contain the line type and the line offset from the subheader begin.
* A line type can have these types: */
enum eact_linetype {
/* contains a calculation */
eact_ltype_calc = 0x03,
/* contains a calculation result (right after the calculation line) */
eact_ltype_calc_result = 0x04,
/* a subcontent, with content header and everything */
eact_ltype_content = 0x06,
/* standard heading - the title surrounded by '=' */
eact_ltype_stdheading = 0x07,
/* a picture - the content is the path to the picture
* ex: '\\fls0\Pict\Pict01.g3p' (With CASIO's encoding on 16-bits) */
eact_ltype_picture = 0x08,
/* Some pure text */
eact_ltype_text = 0x81,
/* Text mixed up with functions -- described by SimLo, untested here */
eact_ltype_code = 0x82
};
/* And here's the line descriptor structure: */
struct line_descriptor {
/* the entry type */
uint8_t entry_type;
/* the entry offset */
uint32_t entry_offset :24;
};
/* And there is only one content after the main header, and it's a content
* of EACT type. But hey, why the heck have we defined several types and stuff?
* Well, that's the funniest thing of E-Activities:
*
* they are RECURSIVE. [drama alert]
*
* Which means in each node, there can be a content to parse. */
#endif /* LIBG1M_FORMAT_EACT_H */

181
include/libg1m/format/mcs.h Normal file
View File

@ -0,0 +1,181 @@
/* ************************************************************************** */
/* _____ _ */
/* libg1m/format/mcs.h |_ _|__ _ _| |__ ___ _ _ */
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
/* | | (_) | |_| | | | | __/ |_| | */
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
/* Last updated: 2016/11/02 14:15:46 |___/ */
/* */
/* ************************************************************************** */
#ifndef LIBG1M_FORMAT_MCS_H
# define LIBG1M_FORMAT_MCS_H
# include <stdint.h>
# pragma pack(1)
/* MCS is the main filesystem on CASIO calculators. They contain settings,
* programs, lists, pictures, captures, matrixes, and other data CASIO
* calculators own.
*
* First of all, here's the BCD structure used pretty much everywhere: */
struct bcd {
/* the BCD value */
uint8_t BCDval[9];
/* and some 4-bytes alignment stuff */
uint8_t align[3];
};
/* So now we are not restricted to only one part. Remember the `number` field
* of the Standard Header? Well, it's the number of parts there is.
* And each part have a header, so let's describe it here.
*/
struct mcs_subheader {
/* main ID ("PROGRAM", zero padded) */
uint8_t intname[16];
/* subitem count: haha, read what follows! */
uint32_t subcount;
};
/* Because yes, an part of an MCS G1M file has subparts!
* Each one of them represent a single file in the MCS.
* According to their type, they are in different directories: */
enum mcs_dirtype {
mcs_dtype_program = 0x1,
mcs_dtype_list = 0x5,
mcs_dtype_mat = 0x6,
mcs_dtype_picture = 0x7,
mcs_dtype_capture = 0xa,
mcs_dtype_spreadsheet = 0xfe,
};
/* And they also have a directory name, which is less precise. Why?
* We don't know! That's the main thing with reverse engineering.
*
* For basic programs, it is: */
# define DIRNAME_PROGRAM "system"
/* For most alpha-mem elements (list, matrixes, pictures): */
# define DIRNAME_ALPHAMEM "main"
/* And for captures: */
# define DIRNAME_CAPTURE "@REV2"
/* Now all of this is said, each part has this header: */
struct mcs_partheader {
/* the directory name (zero-padded).*/
uint8_t dirname[8];
/* the file name (item name) */
uint8_t filename[8];
/* the directory type. */
uint8_t dirtype;
/* the size of the part data, without the part header. */
uint32_t datalength;
/* some alignment */
uint8_t align[3];
};
/* Beneath that, we have specific subsubheaders for specific types.
* Here's what it is for programs: */
struct mcs_programheader {
/* the program password. not encrypted, anything */
uint8_t password[8];
/* and some alignment. */
uint8_t align[2];
};
/* And, well, spreadsheets are more complicated than that.
* There is a header, a column directory and a column definition table.
*
* The header mainly contains the column count: */
struct mcs_spreadsheetheader {
/* magic number? */
uint8_t magic;
/* column count (max: 26) */
uint32_t column_count : 24;
/* alignment or magic? {0, 0, 0, 0} */
uint8_t align[4];
/* number of column definitions */
uint32_t defs_size;
/* alignment or magic II? {0, 0, 0, 0} */
uint8_t align2[4];
};
/* The column definition table is the main definition. It contains the row
* directory, which is a 80-bytes long bitfield indicating if cells are empty
* or not, and the list of non-empty cells.
*
* Each cell is a BCD structure.
*
* The column directory is just a list of 4-bytes sizes of each column,
* counting the row directory and the cells. It's easier to naviguate to
* a column quicker.
*
* Now, what about captures? Well, their header is the following: */
struct mcs_captureheader {
/* the width (0x80) */
uint16_t width;
/* the height (0x40) */
uint16_t height;
};
/* Then the image follows (0x400 for a 0x80*0x40px image).
* A picture is the same that an capture, but without the header and containing
* two 128x64 images (0x800 bytes).
*
* Lists! (had no idea how to introduce them)
* After their header, they have `element_count` BCD list corresponding
* to the numbers real part.
* If at least one of the elements have an imaginary part, there is another
* list right behind of `element_count` BCDs, with all of the imaginary
* parts.
*
* Here's their header: */
struct mcs_listheader {
/* probably the title */
uint8_t title[8];
/* the elements count */
uint16_t element_count;
/* undocumented (always 0?) */
uint8_t undocumented[5];
/* undocumented (either 0x00 or 0x4F) */
uint8_t undocumented2;
};
/* Now, let's see matrixes! */
struct mcs_matheader {
/* undocumented */
uint8_t undocumented[8];
/* height */
uint16_t height;
/* width */
uint16_t width;
/* re-undocumented */
uint8_t undocumented2[4];
};
# pragma pack()
#endif /* LIBG1M_FORMAT_MCS_H */

View File

@ -0,0 +1,70 @@
/* ************************************************************************** */
/* _____ _ */
/* libg1m/format/picture_cg.h |_ _|__ _ _| |__ ___ _ _ */
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
/* | | (_) | |_| | | | | __/ |_| | */
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
/* Last updated: 2016/11/02 14:15:46 |___/ */
/* */
/* ************************************************************************** */
#ifndef LIBG1M_FORMAT_PICTURE_CG_H
# define LIBG1M_FORMAT_PICTURE_CG_H
# include <stdint.h>
/* These are pictures for fx-CG. They only have one part.
*
* Some color depth things: */
enum g3p_colorsize {
g3p_color_4bit = 0x03,
g3p_color_16bit = 0x10
};
/* The subheader is: */
struct g3p_subheader {
/* some magic sequence: "CP0100Ly755"
* "CP" is check by a syscall */
uint8_t magic[11];
/* unused */
uint8_t unused[11];
/* plenty of unused space, or undocumented, for some reason */
uint8_t undocumented[130];
/* magic sequence? "0100" */
uint8_t magic2[4];
/* control sequence? <length of packed data + 4> */
uint32_t control;
/* undocumented, again */
uint16_t undocumented2;
/* dimensions */
uint16_t width;
uint16_t height;
/* color depth - see `g3p_colorsize` */
uint16_t color_depth;
/* undocumented, the third coming */
uint16_t undocumented3;
/* length of the deflated image */
uint32_t dimage_length;
};
/* And after the image: */
struct g3p_footer {
/* magic sequence: "0100" */
uint8_t magic[4];
/* some unused bytes? */
uint8_t unused[0x88];
};
# pragma pack()
#endif /* LIBG1M_FORMAT_PICTURE_CG_H */

View File

@ -10,8 +10,46 @@
#ifndef LIBG1M_INTERNALS_H
# define LIBG1M_INTERNALS_H
# include <libg1m.h>
# include <libg1m/format.h>
# include <libg1m/internals/log.h>
# include <libg1m/internals/format.h>
# include <string.h>
# include <endian.h>
# ifndef min
# define min(A, B) ((A) < (B) ? (A) : (B))
# endif
/* ************************************************************************** */
/* Macros and functions for parsing */
/* ************************************************************************** */
/* read with EOF check */
#define READ(TO, SZ) { \
size_t READ_size = fread(TO, 1, SZ, stream); \
if (READ_size < (SZ)) { \
log_info("READING failed: read %zu/%zu bytes, %zu missing.", \
READ_size, SZ, (SZ) - READ_size); \
return (g1m_error_eof); \
}}
/* read with EOF check, declare var before */
#define DREAD(NAM, STRUCT) \
struct STRUCT NAM; \
READ(&NAM, sizeof(struct STRUCT))
/* skip */
#define SKIP(SZ) \
if (g1m_parse_skip(stream, SZ, NULL)) return (g1m_error_eof);
/* skipping utility */
int g1m_parse_skip(FILE *stream, size_t size, uint_fast32_t *checksum);
/* ************************************************************************** */
/* Specific parsing functions */
/* ************************************************************************** */
int g1m_parse_g3p(g1m_t *handle, FILE *stream,
struct standard_header *std);
int g1m_parse_mcs(g1m_t *handle, FILE *stream, uint_fast16_t num);
int g1m_parse_eact(g1m_t * handle, FILE *stream);
int g1m_parse_addin(g1m_t *handle, FILE *stream);
int g1m_parse_addin_cg(g1m_t *handle, FILE *stream,
struct standard_header *std);
/* ************************************************************************** */
/* Main handle type */

View File

@ -1,543 +0,0 @@
/* ************************************************************************** */
/* _____ _ */
/* libg1m/internals/format.h |_ _|__ _ _| |__ ___ _ _ */
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
/* | | (_) | |_| | | | | __/ |_| | */
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
/* Last updated: 2016/10/30 17:29:42 |___/ */
/* */
/* ************************************************************************** */
#ifndef LIBG1M_INTERNALS_FORMAT_H
# define LIBG1M_INTERNALS_FORMAT_H
# include <stdint.h>
# pragma pack(1)
/* Welcome on this new episode of Monster Circus! Today we'll present to you
* something that you might not be able to forget: the G1M format.
*
* This format is used by CASIO with its calculator for storing things
* like add-ins (compiled programs), MCS (main memory saves), and others.
* Here's a complete list of the declinations and corresponding IDs: */
enum g1m_type {
/* fx-CG add-in */
g1m_type_addin_cg = 0x2c,
/* mcs (main memory) save */
g1m_type_mcs = 0x31,
/* e-activity (document) */
g1m_type_eact = 0x49,
/* g1r equivalent (no idea why it isn't 0x31 '-') */
g1m_type_g2r = 0x62,
/* g3p (fx-CGxx picture) */
g1m_type_g3p = 0x7d,
/* add-in (compiled program) */
g1m_type_addin = 0xf3,
};
/* It all starts with a header, called Standard Header by Simon Lothar.
* This Standard Header contains the total filesize, the G1M type (add-in,
* MCS, e-acts), some data that will be useful for the MCS type, and some
* magic and control bytes.
*
* Keep in mind that multi-byte integers are Big Endian for all of the headers.
* The LSB is the Least Significant Byte: once adapted to the host endianness,
* it can simply be obtained bitwise-AND-ing with 0xff. */
struct standard_header {
/* the file identifier, should be "USBPower". */
uint8_t main_id[8];
/* our filetype! */
uint8_t type;
/* magic numbers, not always the same:
* - for g1m_type_addin_cg: {0x00, 0x01, 0x00, 0x01, 0x00}
* - otherwise: {0x00, 0x10, 0x00, 0x10, 0x00} */
uint8_t magic[5];
/* first control byte: filesize LSB + 0x41 */
uint8_t control;
/* said to be 0x01, but doesn't need to */
uint8_t align_one;
/* total filesize */
uint32_t filesize;
/* second control byte: filesize LSB + 0xb8 */
uint8_t control2;
/* 4-bytes alignment */
uint8_t align[9];
/* number of objects contained (useful for MCS filetype) */
uint16_t number;
};
/* After the Standard Header is read and the G1M type is read, we have parts,
* each with their own subheaders.
* -----------------------------------------------------------------------------
* "Normal" Add-ins (g1a)
* -----------------------------------------------------------------------------
* Add-ins are compiled programs. They only have one part with one subheader,
* which is the following: */
struct g1a_subheader {
/* the internal name, of format "@APPNAME".
* useful for add-ins calling themselves... I guess? */
uint8_t internal_name[8];
/* the number of estrips (I don't know yet) */
uint8_t estrips_count;
/* the add-in version, of format "01.23.4567"
* the "01.23" will be displayed in SYSTEM > VERSION */
uint8_t version[10];
/* the add-in creation type, of format "YYYY.MMDD.HHMM" */
uint8_t creation_date[14];
/* 30x17 pixel menu icon bitmap */
uint8_t icon[68];
/* program title */
uint8_t title[8];
/* and the filesize of the part! */
uint32_t filesize;
};
/* Then the G1A file will just contain the add-in code and stop.
* -----------------------------------------------------------------------------
* fx-CG Add-ins (g3a)
* -----------------------------------------------------------------------------
* fx-CG add-ins also have one part only. */
struct g3a_subheader {
/* byte checksum: 0x0000 to 0x001F (standard header)
* and 0x0024 to EOF (everything after this checksum), minus 4. */
uint32_t checksum;
/* magic sequence: {0x01, 0x01} */
uint8_t magic[2];
/* undocumented */
uint8_t undocumented[8];
/* control: filesize - 0x7000 - 4 */
uint32_t control;
/* undocumented */
uint8_t undocumented2[14];
/* title (zero terminated) */
uint8_t title[16];
/* undocumented */
uint8_t undocumented3[12];
/* filesize */
uint32_t filesize;
/* internal name (preceded by @, filled up with zeros */
uint8_t intname[11];
/* language labels */
uint8_t language_1_label[24];
uint8_t language_2_label[24];
uint8_t language_3_label[24];
uint8_t language_4_label[24];
uint8_t language_5_label[24];
uint8_t language_6_label[24];
uint8_t language_7_label[24];
uint8_t language_8_label[24];
/* eAct strip flag (0x00: cannot be used, 0x01: can be used) */
uint8_t eact_strip_flag;
/* some null bytes */
uint8_t zeroes[4];
/* version: "01.00.0000" then zeroes */
uint8_t version[12];
/* timestamp: "2012.0903.1652" */
uint8_t stamp[14];
/* null bytes, again */
uint8_t zeroes2[38];
/* eAct strip labels */
uint8_t eact_strip_label_1[36];
uint8_t eact_strip_label_2[36];
uint8_t eact_strip_label_3[36];
uint8_t eact_strip_label_4[36];
uint8_t eact_strip_label_5[36];
uint8_t eact_strip_label_6[36];
uint8_t eact_strip_label_7[36];
uint8_t eact_strip_label_8[36];
/* eAct icon */
uint8_t icon[0x300];
/* unused */
uint8_t unused[0x92c];
/* G3A filename (seriously?!) */
uint8_t g3a_filename[0x144];
/* selected and unselected icon image */
uint8_t selected_icon_image[0x3000];
uint8_t unselected_icon_image[0x3000];
};
/* -----------------------------------------------------------------------------
* MCS saves (g1m, g1r)
* -----------------------------------------------------------------------------
* MCS is the main filesystem on CASIO calculators. They contain settings,
* programs, lists, pictures, captures, matrixes, and other data CASIO
* calculators own.
*
* First of all, here's the BCD structure used pretty much everywhere: */
struct bcd {
/* the BCD value */
uint8_t BCDval[9];
/* and some 4-bytes alignment stuff */
uint8_t align[3];
};
/* So now we are not restricted to only one part. Remember the `number` field
* of the Standard Header? Well, it's the number of parts there is.
* And each part have a header, so let's describe it here.
*/
struct mcs_subheader {
/* main ID ("PROGRAM", zero padded) */
uint8_t intname[16];
/* subitem count: haha, read what follows! */
uint32_t subcount;
};
/* Because yes, an part of an MCS G1M file has subparts!
* Each one of them represent a single file in the MCS.
* According to their type, they are in different directories: */
enum mcs_dirtype {
mcs_dtype_program = 0x1,
mcs_dtype_list = 0x5,
mcs_dtype_mat = 0x6,
mcs_dtype_picture = 0x7,
mcs_dtype_capture = 0xa,
mcs_dtype_spreadsheet = 0xfe,
};
/* And they also have a directory name, which is less precise. Why?
* We don't know! That's the main thing with reverse engineering.
*
* For basic programs, it is: */
# define DIRNAME_PROGRAM "system"
/* For most alpha-mem elements (list, matrixes, pictures): */
# define DIRNAME_ALPHAMEM "main"
/* And for captures: */
# define DIRNAME_CAPTURE "@REV2"
/* Now all of this is said, each part has this header: */
struct mcs_partheader {
/* the directory name (zero-padded).*/
uint8_t dirname[8];
/* the file name (item name) */
uint8_t filename[8];
/* the directory type. */
uint8_t dirtype;
/* the size of the part data, without the part header. */
uint32_t datalength;
/* some alignment */
uint8_t align[3];
};
/* Beneath that, we have specific subsubheaders for specific types.
* Here's what it is for programs: */
struct mcs_programheader {
/* the program password. not encrypted, anything */
uint8_t password[8];
/* and some alignment. */
uint8_t align[2];
};
/* And, well, spreadsheets are more complicated than that.
* There is a header, a column directory and a column definition table.
*
* The header mainly contains the column count: */
struct mcs_spreadsheetheader {
/* magic number? */
uint8_t magic;
/* column count (max: 26) */
uint32_t column_count : 24;
/* alignment or magic? {0, 0, 0, 0} */
uint8_t align[4];
/* number of column definitions */
uint32_t defs_size;
/* alignment or magic II? {0, 0, 0, 0} */
uint8_t align2[4];
};
/* The column definition table is the main definition. It contains the row
* directory, which is a 80-bytes long bitfield indicating if cells are empty
* or not, and the list of non-empty cells.
*
* Each cell is a BCD structure.
*
* The column directory is just a list of 4-bytes sizes of each column,
* counting the row directory and the cells. It's easier to naviguate to
* a column quicker.
*
* Now, what about captures? Well, their header is the following: */
struct mcs_captureheader {
/* the width (0x80) */
uint16_t width;
/* the height (0x40) */
uint16_t height;
};
/* Then the image follows (0x400 for a 0x80*0x40px image).
* A picture is the same that an capture, but without the header and containing
* two 128x64 images (0x800 bytes).
*
* Lists! (had no idea how to introduce them)
* After their header, they have `element_count` BCD list corresponding
* to the numbers real part.
* If at least one of the elements have an imaginary part, there is another
* list right behind of `element_count` BCDs, with all of the imaginary
* parts.
*
* Here's their header: */
struct mcs_listheader {
/* probably the title */
uint8_t title[8];
/* the elements count */
uint16_t element_count;
/* undocumented (always 0?) */
uint8_t undocumented[5];
/* undocumented (either 0x00 or 0x4F) */
uint8_t undocumented2;
};
/* Now, let's see matrixes! */
struct mcs_matheader {
/* undocumented */
uint8_t undocumented[8];
/* height */
uint16_t height;
/* width */
uint16_t width;
/* re-undocumented */
uint8_t undocumented2[4];
};
/* -----------------------------------------------------------------------------
* E-Activities (g1e, g2e, g3e)
* -----------------------------------------------------------------------------
* E-Activities are the format CASIO uses for in-calc documents.
* It is the funniest subformat the the libg1m can parse.
*
* There is only one part for e-activities.
* The overall header has two zones:
* - the special header, that keeps the global info;
* - the setup area, that keeps one part of the calculator setup.
*
* There are several types for E-Activies, identified by the extensions
* CASIO give them.
* The E-Act version in the header is 0x10100 for g1e, 0x10200 for g2e,
* and 0x10400 for g3e. */
# define EACT_G1E 0x10100
# define EACT_G2E 0x10200
# define EACT_G3E 0x10400
/* The differences between these formats are the following:
* - On g1e, the OS version is always 0x5061636B, where on g2e/g3e,
* the OS version is the version of the OS on which the file was written.
* - On g1e/g2e, the setup directory is 0x10-bytes long, where on g3e,
* the setup directory is 0x2C-bytes long.
*
* So the main header is: */
struct eact_header {
/* the filesize */
uint32_t filesize;
/* the size of the setup area */
uint32_t setup_area_size;
/* the E-Act version */
uint32_t eact_version;
/* The OS version - only with G2E/G3E */
uint32_t os_version;
};
/* And now, the funniest part: the content.
* So a content has this header: */
struct eact_contentheader {
/* content type: @EACT, @RUNMAT, ... */
uint8_t type[8];
/* align for element name */
uint8_t align[8];
/* element name: "EACT1", "TEXT1", ... */
uint8_t name[16];
/* align for subheader */
uint8_t align2[4];
};
/* There are several types of contents.
* The most basic one is also called "@EACT".
* It has this content subheader: */
struct eact_eactheader {
/* line count */
uint32_t line_count;
};
/* And after, lines come. First of all, there is a line descriptor table,
* that contain the line type and the line offset from the subheader begin.
* A line type can have these types: */
enum eact_linetype {
/* contains a calculation */
eact_ltype_calc = 0x03,
/* contains a calculation result (right after the calculation line) */
eact_ltype_calc_result = 0x04,
/* a subcontent, with content header and everything */
eact_ltype_content = 0x06,
/* standard heading - the title surrounded by '=' */
eact_ltype_stdheading = 0x07,
/* a picture - the content is the path to the picture
* ex: '\\fls0\Pict\Pict01.g3p' (With CASIO's encoding on 16-bits) */
eact_ltype_picture = 0x08,
/* Some pure text */
eact_ltype_text = 0x81,
/* Text mixed up with functions -- described by SimLo, untested here */
eact_ltype_code = 0x82
};
/* And here's the line descriptor structure: */
struct line_descriptor {
/* the entry type */
uint8_t entry_type;
/* the entry offset */
uint32_t entry_offset :24;
};
/* And there is only one content after the main header, and it's a content
* of EACT type. But hey, why the heck have we defined several types and stuff?
* Well, that's the funniest thing of E-Activities:
*
* they are RECURSIVE. [drama alert]
*
* Which means in each node, there can be a content to parse. */
/* -----------------------------------------------------------------------------
* fx-CG Pictures (g3p)
* -----------------------------------------------------------------------------
* These are pictures for fx-CG. They only have one part.
*
* Some color depth things: */
enum g3p_colorsize {
g3p_color_4bit = 0x03,
g3p_color_16bit = 0x10
};
/* The subheader is: */
struct g3p_subheader {
/* some magic sequence: "CP0100Ly755"
* "CP" is check by a syscall */
uint8_t magic[11];
/* unused */
uint8_t unused[11];
/* plenty of unused space, or undocumented, for some reason */
uint8_t undocumented[130];
/* magic sequence? "0100" */
uint8_t magic2[4];
/* control sequence? <length of packed data + 4> */
uint32_t control;
/* undocumented, again */
uint16_t undocumented2;
/* dimensions */
uint16_t width;
uint16_t height;
/* color depth - see `g3p_colorsize` */
uint16_t color_depth;
/* undocumented, the third coming */
uint16_t undocumented3;
/* length of the deflated image */
uint32_t dimage_length;
};
/* And after the image: */
struct g3p_footer {
/* magic sequence: "0100" */
uint8_t magic[4];
/* some unused bytes? */
uint8_t unused[0x88];
};
# pragma pack()
/* Credits: most of the things described here were described by Simon Lothar.
* Thanks to Zezombye for the List and Matrixes format. */
#endif /* LIBG1M_INTERNALS_FORMAT_H */

View File

@ -9,6 +9,7 @@
/* ************************************************************************** */
#ifndef LIBG1M_INTERNALS_LOG_H
# define LIBG1M_INTERNALS_LOG_H
# include <stdint.h>
/* ************************************************************************** */
/* Log utility */

View File

@ -8,6 +8,8 @@
/* */
/* ************************************************************************** */
#include <libg1m/internals.h>
#include <libg1m/format.h>
#include <stdint.h>
#include <string.h>
#include <ctype.h>
#define min(A, B) ((A) < (B) ? (A) : (B))

View File

@ -1,752 +0,0 @@
/* ************************************************************************** */
/* _____ _ */
/* parse.c |_ _|__ _ _| |__ ___ _ _ */
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
/* | | (_) | |_| | | | | __/ |_| | */
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
/* Last updated: 2016/10/30 18:24:25 |___/ */
/* */
/* ************************************************************************** */
#include <libg1m/internals.h>
#include <string.h>
#include <endian.h>
/* ************************************************************************** */
/* Useful macros */
/* ************************************************************************** */
#define min(A, B) ((A) < (B) ? (A) : (B))
/* read with EOF check */
#define READ(TO, SZ) { \
size_t READ_size = fread(TO, 1, SZ, stream); \
if (READ_size < (SZ)) { \
log_info("READING failed: read %zu/%zu bytes, %zu missing.", \
READ_size, SZ, (SZ) - READ_size); \
return (g1m_error_eof); \
}}
/* read with EOF check, declare var before */
#define DREAD(NAM, STRUCT) \
struct STRUCT NAM; \
READ(&NAM, sizeof(struct STRUCT))
/* skip */
#define SKIP(SZ) \
if (skip(stream, SZ, NULL)) return (g1m_error_eof);
/* ************************************************************************** */
/* Skip utilities */
/* ************************************************************************** */
/**
* skip:
* Skip some bytes.
*
* I've made two loops to avoid conditions in each loop iteration
* when checksum is not calculated.
*
* @arg stream the stream where to skip bytes.
* @arg size the size to skip.
* @arg checksum pointer to the checksum variable if there is one.
*/
static int skip(FILE *stream, size_t size, uint_fast32_t *checksum)
{
uint8_t buf[1024];
size_t size_ini = size;
if (checksum) {
uint_fast32_t add = 0;
while (1) {
/* if no size left, exit */
if (!size) break;
/* read that much */
{
size_t curlen = min(size, 1024);
size_t read_len = fread(buf, 1, curlen, stream);
size -= read_len;
if (read_len < curlen) {
log_info("SKIPPING failed, skipped %zu/%zu bytes, "
"%zu missing", size_ini - size, size_ini, size);
return (g1m_error_eof);
}
/* feed the checksum */
for (size_t i = 0; i < curlen; i++)
add += buf[i];
}
}
*checksum += add;
} else {
while (1) {
/* if no size left, exit */
if (!size) break;
/* read that much */
{
size_t curlen = min(size, 1024);
size_t read_len = fread(buf, 1, curlen, stream);
size -= read_len;
if (read_len < curlen) {
log_info("SKIPPING failed, skipped %zu/%zu bytes, "
"%zu missing", size_ini - size, size_ini, size);
return (g1m_error_eof);
}
}
}
}
/* no error */
return (0);
}
/* ************************************************************************** */
/* fx-CG Add-ins (g3a) */
/* ************************************************************************** */
/**
* g1m_parse_addin_cg:
* Parse fx-CG add-in (after Standard Header).
*
* @arg handle the handle.
* @arg stream the stream to parse from.
* @arg std the standard header.
* @return the error code (0 if ok).
*/
static int g1m_parse_addin_cg(g1m_t *handle, FILE *stream,
struct standard_header *std)
{
/* get the subheader */
DREAD(hd, g3a_subheader)
/* correct subheader endianness */
hd.checksum = be32toh(hd.checksum);
hd.control = be32toh(hd.control);
hd.filesize = be32toh(hd.filesize);
/* check the control */
if (hd.control != hd.filesize - 0x7000 - 4) {
log_info("control value is incorrect");
return (g1m_error_magic);
}
/* skip while making checksum */
uint_fast32_t checksum = 0;
for (size_t i = 0; i < sizeof(struct standard_header); i++)
checksum += ((uint8_t*)std)[i];
for (size_t i = 4; i < sizeof(struct g3a_subheader); i++)
checksum += ((uint8_t*)&hd)[i];
if (skip(stream, hd.filesize - sizeof(struct g3a_subheader), &checksum))
return (g1m_error_eof);
checksum -= 4;
/* check the checksum */
if (checksum != hd.checksum) {
log_info("Invalid checksum.");
return (g1m_error_magic);
}
/* log */
log_info("title is '%s'", hd.title);
log_info("internal name is '%s'", hd.intname);
log_info("version is '%s'", hd.version);
log_info("timestamp is '%s'", hd.stamp);
log_info("G3A filename is '%s' (yea srsly)", hd.g3a_filename);
/* no error */
return (0);
}
/* ************************************************************************** */
/* Add-ins (g1a) */
/* ************************************************************************** */
/**
* g1m_parse_addin:
* Parse "normal" add-in (after Standard Header).
*
* @arg handle the handle.
* @arg stream the stream to parse from.
* @return the error code (0 if ok).
*/
static int g1m_parse_addin(g1m_t *handle, FILE *stream)
{
/* get the subheader */
DREAD(hd, g1a_subheader)
/* correct subheader endianess */
hd.filesize = be32toh(hd.filesize);
/* log info about the subheader */
log_info("internal name is '%.8s'", hd.internal_name);
log_info("estrips count is %hhu", hd.estrips_count);
log_info("version is %.10s", hd.version);
log_info("creation date is %.14s", hd.creation_date);
/* skip size */
SKIP(hd.filesize - sizeof(struct standard_header)
- sizeof(struct g1a_subheader))
/* no errors */
return (0);
}
/* ************************************************************************** */
/* MCS saves (g1m, g1r) */
/* ************************************************************************** */
/**
* g1m_parse_mcs_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 g1m_parse_mcs_program(g1m_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);
/* seek for what's left in the file */
SKIP(length - sizeof(struct mcs_programheader))
/* no error */
return (0);
}
/**
* g1m_parse_mcs_spreadsheet:
* Parse a spreadsheet.
*
* @arg handle the handle.
* @arg stream the stream to parse from.
* @return the error code (0 if ok).
*/
static int g1m_parse_mcs_spreadsheet(g1m_t *handle, FILE *stream)
{
/* read header */
DREAD(hd, mcs_spreadsheetheader)
/* correct endianess */
uint_fast32_t colcount = be32toh(hd.column_count << 8);
hd.defs_size = be32toh(hd.defs_size);
/* log some info */
log_info("%ld columns to parse!", colcount);
/* get the column directory */
uint32_t column_directory[colcount];
READ(&column_directory, sizeof(uint32_t) * colcount)
/* - we should use `be32toh` here, but it isn't necessary for bool check. */
/* 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)
/* log it */
log_info("[%ld, %ld] not empty.", c, i);
}
/* iterate bit and rd */
rd += (bit & 1);
bit = (bit >> 1) | ((bit & 1) << 7);
}
}
/* no error */
return (0);
}
/**
* g1m_parse_mcs_list:
* Parse an List.
*
* @arg handle the handle.
* @arg stream the stream to parse from.
* @arg size the
* @return the error code (0 if ok).
*/
static int g1m_parse_mcs_list(g1m_t *handle, FILE *stream, uint_fast32_t size)
{
/* read header */
DREAD(hd, mcs_listheader)
/* correct endianess */
uint16_t elcount = hd.element_count;
elcount = be16toh(elcount);
/* prepare browsing */
size -= sizeof(struct mcs_listheader);
size_t elsize = sizeof(struct bcd) * elcount;
/* make tabs */
struct bcd real[elcount];
struct bcd imgn[elcount];
bzero(real, elsize);
bzero(imgn, elsize);
/* browse */
log_info("Browsing %d list elements", elcount);
READ(&real, elsize)
if (size > elsize) /* FIXME: HACK */
READ(&imgn, elsize)
/* no error */
return (0);
}
/**
* g1m_parse_mcs_mat:
* Parse a Matrix.
*
* @arg handle the handle.
* @arg stream the stream to parse from.
* @return the error code (0 if ok).
*/
static int g1m_parse_mcs_mat(g1m_t *handle, FILE *stream)
{
/* read header */
DREAD(hd, mcs_matheader)
/* correct endianess */
hd.width = be16toh(hd.width);
hd.height = be16toh(hd.height);
/* log info */
log_info("Matrix is %d/%d", hd.width, hd.height);
/* parse cells */
size_t num = hd.width * hd.height;
for (size_t i = 0; i < num; i++) {
/* read the cell */
DREAD(cell, bcd)
/* get x using `i % height` and y using `i / height` */
}
/* no error */
return (0);
}
/**
* g1m_parse_mcs_capture:
* Parse a capture.
*
* @arg handle the handle.
* @arg stream the stream to parse from.
* @return the error code (0 if ok).
*/
static int g1m_parse_mcs_capture(g1m_t *handle, FILE *stream)
{
/* read header */
DREAD(hd, mcs_captureheader)
/* correct endianess */
hd.width = be16toh(hd.width);
hd.height = be16toh(hd.height);
/* print info */
log_info("capture is %ux%u sized", hd.width, hd.height);
/* skip */
SKIP(hd.height * (hd.width / 8 + !!(hd.width % 8)))
/* no error */
return (0);
}
/**
* g1m_parse_mcs:
* We have passed the Standard Header and it's a g... MCS! Let's parse it!
*
* @arg handle the handle.
* @arg stream the stream to parse from.
* @arg num number of sizes.
* @return the error code (0 if ok).
*/
static int g1m_parse_mcs(g1m_t *handle, FILE *stream, uint_fast16_t num)
{
log_info("%ld parts to browse", num);
/* read all of the parts */
for (uint_fast16_t i = 0; i < num; i++) {
/* get the subheader */
DREAD(hd, mcs_subheader)
/* correct endianess */
hd.subcount = be32toh(hd.subcount);
/* log info about part */
log_info("[%ld] Internal name is '%.16s'", i, hd.intname);
log_info("[%ld] %d subparts to browse", i, hd.subcount);
/* foreach subpart */
uint_fast32_t subcount = hd.subcount;
for (uint_fast32_t j = 0; j < subcount; j++) {
/* get the part header */
DREAD(phd, mcs_partheader)
/* correct endianess */
phd.datalength = be32toh(phd.datalength);
/* log info about the subpart */
log_info("[%ld,%ld] directory name is '%.8s'", i, j, phd.dirname);
log_info("[%ld,%ld] filename is '%.8s'", i, j, phd.filename);
log_info("[%ld,%ld] directory type is '%s' (0x%02d)",
i, j, g1m_get_dtype_string(phd.dirtype), phd.dirtype);
log_info("[%ld,%ld] data length is %u", i, j, phd.datalength);
/* and read that much data */
int err = 0;
switch (phd.dirtype) {
case mcs_dtype_program:
err = g1m_parse_mcs_program(handle, stream, phd.datalength);
break;
case mcs_dtype_spreadsheet:
err = g1m_parse_mcs_spreadsheet(handle, stream);
break;
case mcs_dtype_capture:
err = g1m_parse_mcs_capture(handle, stream);
break;
case mcs_dtype_list:
err = g1m_parse_mcs_list(handle, stream, phd.datalength);
break;
case mcs_dtype_mat:
err = g1m_parse_mcs_mat(handle, stream);
break;
default:
SKIP(phd.datalength)
}
/* if there is an error, return it */
if (err) return (err);
}
}
/* no error */
return (0);
}
/* ************************************************************************** */
/* Prizm pictures */
/* ************************************************************************** */
/**
* g1m_parse_g3p:
* Parse a G3P file.
*
* @arg handle the handle.
* @arg stream the stream to parse from.
* @arg std the standard header.
* @return the error code (0 if ok).
*/
static int g1m_parse_g3p(g1m_t *handle, FILE *stream,
struct standard_header *std)
{
/* get the header and footer */
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 */
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");
return (g1m_error_magic);
}
/* TODO: what about "length of packed data + 4" control? */
/* TODO: get footer, check control 0100 */
/* check if image is encrypted */
int is_encrypted = (((std->filesize & 0xff) +
((std->filesize & 0xff00) >> 8) + g1m_type_g3p) & 0xff) ^ 0x1c;
/* log info */
log_info("Width: %dpx, height: %dpx", hd.width, hd.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");
/* no error */
return (0);
}
/* ************************************************************************** */
/* E-Activities */
/* ************************************************************************** */
/* prototype for content parsing */
static int g1m_parse_eact_cont(g1m_t *handle, uint8_t *buf, size_t bufsize,
int depth);
/**
* g1m_parse_eact_eact:
* Parse an EACT in an EACT, from the subheader.
*
* @arg handle the handle.
* @arg buf the buffer.
* @arg bufsize the buffer size.
* @arg depth the current depth.
* @return the error code (0 if ok).
*/
static int g1m_parse_eact_eact(g1m_t *handle, uint8_t *buf, size_t bufsize,
int depth)
{
int err;
/* get e-act subheader */
if (bufsize < sizeof(struct eact_eactheader)) return (g1m_error_eof);
struct eact_eactheader ehd;
memcpy(&ehd, buf, sizeof(struct eact_eactheader));
/* correct endianess */
ehd.line_count = be32toh(ehd.line_count);
/* get the line descriptors
* there is actually a "bonus" line descriptor at the end, but the
* fact that we're using offsets lets us unaware of it. */
if (bufsize < sizeof(struct eact_eactheader)
+ (ehd.line_count + 1) * sizeof(struct line_descriptor))
return (g1m_error_eof);
struct line_descriptor *lds = (void*)&buf[sizeof(struct eact_eactheader)];
/* browse the lines */
log_info("[>%d] %d lines to browse", depth, ehd.line_count);
if (ehd.line_count)
lds[0].entry_offset = be32toh(lds[0].entry_offset << 8);
for (uint_fast32_t i = 0; i < ehd.line_count; i++) {
/* get line size
* for this, we'll calculate the distance to the next line offset.
* if there is no next line (last line), then the data left will be
* attributed to the line. */
size_t linesize;
if (i == ehd.line_count - 1)
linesize = bufsize - lds[i].entry_offset;
else {
/* correct endianess of the next element */
lds[i + 1].entry_offset = be32toh(lds[i + 1].entry_offset << 8);
/* then calculate */
linesize = lds[i + 1].entry_offset - lds[i].entry_offset;
}
/* store current info */
uint_fast8_t entry_type = lds[i].entry_type;
uint_fast32_t entry_offset = lds[i].entry_offset;
/* check if buffer is big enough */
if (entry_offset + linesize > bufsize)
return (g1m_error_eof);
/* log data */
log_info("[>%d][%ld] Is '%s' (0x%02x)", depth, i + 1,
g1m_get_eact_ltype_string(entry_type), entry_type);
#if LOGLEVEL <= ll_info
if (entry_type != eact_ltype_content) {
log_info("[>%d][%ld] Buffer (0x%zxo) is:", depth, i + 1, linesize);
logm_info(&buf[entry_offset], linesize);
}
#endif
/* go recursive */
if (lds[i].entry_type == eact_ltype_content
&& (err = g1m_parse_eact_cont(handle, &buf[entry_offset], linesize,
depth + 1)))
return (err);
}
/* no error */
return (0);
}
/**
* g1m_parse_eact_cont:
* Parse an E-Activity content.
*
* @arg handle the handle.
* @arg buf the buffer.
* @arg bufsize the buffer length.
* @arg depth the current depth.
* @return the error code (0 if ok).
*/
static int g1m_parse_eact_cont(g1m_t *handle, uint8_t *buf, size_t bufsize,
int depth)
{
(void)depth;
/* read content header */
size_t could_read = min(bufsize, sizeof(struct eact_contentheader));
if (could_read < 9) return (g1m_error_eof);
struct eact_contentheader hd = {};
memcpy(&hd, buf, could_read);
/* log info */
log_info("[>%d] Type is '%.8s'", depth, hd.type);
log_info("[>%d] Name is '%.16s'", depth, hd.name);
/* prepare for next */
if (bufsize == could_read)
return (0);
buf += could_read;
bufsize -= could_read;
/* if type eact, parse eact */
if (!strcmp((char*)hd.type, "@EACT"))
return (g1m_parse_eact_eact(handle, buf, bufsize, depth));
/* otherwise, no error. */
return (0);
}
/**
* g1m_parse_eact:
* Parse an EACT.
*
* Thanks to Julese50/CoiledSpring for his help on e-acts parsing.
*
* @arg handle the handle.
* @arg stream the stream to parse from.
* @return the error code (0 if ok).
*/
static int g1m_parse_eact(g1m_t * handle, FILE *stream)
{
/* parse the header */
DREAD(hd, eact_header)
/* correct endianess */
hd.filesize = be32toh(hd.filesize);
hd.setup_area_size = be32toh(hd.setup_area_size);
hd.eact_version = be32toh(hd.eact_version);
hd.os_version = be32toh(hd.os_version);
/* find out the size of the setup area */
log_info("E-Activity version is '%s'.",
hd.eact_version == EACT_G1E ? "g1e" :
hd.eact_version == EACT_G2E ? "g2e" : "g3e");
log_info("Setup area size is 0x%x bytes long.",
hd.setup_area_size);
/* skip the setup area */
SKIP(hd.setup_area_size)
/* get content buffer */
size_t bufsize = hd.filesize - sizeof(struct standard_header)
- sizeof(struct eact_header) - hd.setup_area_size;
uint8_t buf[bufsize];
READ(&buf, bufsize)
/* parse content */
return (g1m_parse_eact_cont(handle, buf, bufsize, 0));
}
/* ************************************************************************** */
/* Main parsing */
/* ************************************************************************** */
/**
* g1m_parse:
* Parse a G1M file.
*
* Read the standard header, correct endianness, check magic numbers,
* then read subparts according to the G1M type.
*
* @arg handle the handle.
* @arg stream the stream to parse from.
* @return the error code (0 if ok).
*/
int g1m_parse(g1m_t *handle, FILE *stream)
{
/* get the standard header */
DREAD(hd, standard_header)
/* reverse */
uint8_t *u = (uint8_t*)&hd;
for (unsigned int i = 0; i < sizeof(struct standard_header); i++)
u[i] = ~u[i];
/* print header */
log_info("Raw standard header is:");
logm_info(&hd, sizeof(struct standard_header));
/* correct standard header endianess */
hd.filesize = be32toh(hd.filesize);
hd.number = be16toh(hd.number);
/* what is the expected magic sequence? */
const uint8_t magic_sequence_cg[] = {0x00, 0x01, 0x00, 0x01, 0x00};
const uint8_t magic_sequence_0[] = {0x00, 0x10, 0x00, 0x10, 0x00};
const uint8_t *magic_sequence =
(hd.type == g1m_type_addin_cg) ? magic_sequence_cg : magic_sequence_0;
/* check standard header magics */
if (memcmp(hd.magic, magic_sequence, 5)) {
log_info("Magic sequence isn't right.");
return (g1m_error_magic);
} else if (hd.control != ((hd.filesize + 0x41) & 0xff)) {
log_info("First control byte isn't right.");
return (g1m_error_magic);
} else if (hd.control2 != ((hd.filesize + 0xb8) & 0xff)) {
log_info("Second control byte isn't right.");
return (g1m_error_magic);
}
/* log some data */
log_info("Standard Header Main ID is '%.8s'", hd.main_id);
log_info("Standard Header Type is '%s' (0x%02x)",
g1m_get_type_string(hd.type), hd.type);
log_info("Standard Header filesize is %uo", hd.filesize);
log_info("Standard Header num is %d.", hd.number);
/* subparse. */
switch (hd.type) {
case g1m_type_addin:
return (g1m_parse_addin(handle, stream));
case g1m_type_addin_cg:
return (g1m_parse_addin_cg(handle, stream, &hd));
case g1m_type_g2r: case g1m_type_mcs:
return (g1m_parse_mcs(handle, stream, hd.number));
case g1m_type_eact:
return (g1m_parse_eact(handle, stream));
case g1m_type_g3p:
return (g1m_parse_g3p(handle, stream, &hd));
default: break;
}
/* no error */
return (0);
}

41
src/parse/addin.c Normal file
View File

@ -0,0 +1,41 @@
/* ************************************************************************** */
/* _____ _ */
/* parse/addin.c |_ _|__ _ _| |__ ___ _ _ */
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
/* | | (_) | |_| | | | | __/ |_| | */
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
/* Last updated: 2016/11/02 14:47:49 |___/ */
/* */
/* ************************************************************************** */
#include <libg1m/internals.h>
/**
* g1m_parse_addin:
* Parse "normal" add-in (after Standard Header).
*
* @arg handle the handle.
* @arg stream the stream to parse from.
* @return the error code (0 if ok).
*/
int g1m_parse_addin(g1m_t *handle, FILE *stream)
{
/* get the subheader */
DREAD(hd, g1a_subheader)
/* correct subheader endianess */
hd.filesize = be32toh(hd.filesize);
/* log info about the subheader */
log_info("internal name is '%.8s'", hd.internal_name);
log_info("estrips count is %hhu", hd.estrips_count);
log_info("version is %.10s", hd.version);
log_info("creation date is %.14s", hd.creation_date);
/* skip size */
SKIP(hd.filesize - sizeof(struct standard_header)
- sizeof(struct g1a_subheader))
/* no errors */
return (0);
}

65
src/parse/addin_cg.c Normal file
View File

@ -0,0 +1,65 @@
/* ************************************************************************** */
/* _____ _ */
/* parse/addin_cg.c |_ _|__ _ _| |__ ___ _ _ */
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
/* | | (_) | |_| | | | | __/ |_| | */
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
/* Last updated: 2016/11/02 14:47:49 |___/ */
/* */
/* ************************************************************************** */
#include <libg1m/internals.h>
/**
* g1m_parse_addin_cg:
* Parse fx-CG add-in (after Standard Header).
*
* @arg handle the handle.
* @arg stream the stream to parse from.
* @arg std the standard header.
* @return the error code (0 if ok).
*/
int g1m_parse_addin_cg(g1m_t *handle, FILE *stream,
struct standard_header *std)
{
/* get the subheader */
DREAD(hd, g3a_subheader)
/* correct subheader endianness */
hd.checksum = be32toh(hd.checksum);
hd.control = be32toh(hd.control);
hd.filesize = be32toh(hd.filesize);
/* check the control */
if (hd.control != hd.filesize - 0x7000 - 4) {
log_info("control value is incorrect");
return (g1m_error_magic);
}
/* skip while making checksum */
uint_fast32_t checksum = 0;
for (size_t i = 0; i < sizeof(struct standard_header); i++)
checksum += ((uint8_t*)std)[i];
for (size_t i = 4; i < sizeof(struct g3a_subheader); i++)
checksum += ((uint8_t*)&hd)[i];
if (g1m_parse_skip(stream, hd.filesize - sizeof(struct g3a_subheader),
&checksum))
return (g1m_error_eof);
checksum -= 4;
/* check the checksum */
if (checksum != hd.checksum) {
log_info("Invalid checksum.");
return (g1m_error_magic);
}
/* log */
log_info("title is '%s'", hd.title);
log_info("internal name is '%s'", hd.intname);
log_info("version is '%s'", hd.version);
log_info("timestamp is '%s'", hd.stamp);
log_info("G3A filename is '%s' (yea srsly)", hd.g3a_filename);
/* no error */
return (0);
}

176
src/parse/eact.c Normal file
View File

@ -0,0 +1,176 @@
/* ************************************************************************** */
/* _____ _ */
/* parse/eact.c |_ _|__ _ _| |__ ___ _ _ */
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
/* | | (_) | |_| | | | | __/ |_| | */
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
/* Last updated: 2016/11/02 14:47:49 |___/ */
/* */
/* ************************************************************************** */
#include <libg1m/internals.h>
/* prototype for content parsing */
static int g1m_parse_eact_cont(g1m_t *handle, uint8_t *buf, size_t bufsize,
int depth);
/**
* g1m_parse_eact_eact:
* Parse an EACT in an EACT, from the subheader.
*
* @arg handle the handle.
* @arg buf the buffer.
* @arg bufsize the buffer size.
* @arg depth the current depth.
* @return the error code (0 if ok).
*/
static int g1m_parse_eact_eact(g1m_t *handle, uint8_t *buf, size_t bufsize,
int depth)
{
int err;
/* get e-act subheader */
if (bufsize < sizeof(struct eact_eactheader)) return (g1m_error_eof);
struct eact_eactheader ehd;
memcpy(&ehd, buf, sizeof(struct eact_eactheader));
/* correct endianess */
ehd.line_count = be32toh(ehd.line_count);
/* get the line descriptors
* there is actually a "bonus" line descriptor at the end, but the
* fact that we're using offsets lets us unaware of it. */
if (bufsize < sizeof(struct eact_eactheader)
+ (ehd.line_count + 1) * sizeof(struct line_descriptor))
return (g1m_error_eof);
struct line_descriptor *lds = (void*)&buf[sizeof(struct eact_eactheader)];
/* browse the lines */
log_info("[>%d] %d lines to browse", depth, ehd.line_count);
if (ehd.line_count)
lds[0].entry_offset = be32toh(lds[0].entry_offset << 8);
for (uint_fast32_t i = 0; i < ehd.line_count; i++) {
/* get line size
* for this, we'll calculate the distance to the next line offset.
* if there is no next line (last line), then the data left will be
* attributed to the line. */
size_t linesize;
if (i == ehd.line_count - 1)
linesize = bufsize - lds[i].entry_offset;
else {
/* correct endianess of the next element */
lds[i + 1].entry_offset = be32toh(lds[i + 1].entry_offset << 8);
/* then calculate */
linesize = lds[i + 1].entry_offset - lds[i].entry_offset;
}
/* store current info */
uint_fast8_t entry_type = lds[i].entry_type;
uint_fast32_t entry_offset = lds[i].entry_offset;
/* check if buffer is big enough */
if (entry_offset + linesize > bufsize)
return (g1m_error_eof);
/* log data */
log_info("[>%d][%ld] Is '%s' (0x%02x)", depth, i + 1,
g1m_get_eact_ltype_string(entry_type), entry_type);
#if LOGLEVEL <= ll_info
if (entry_type != eact_ltype_content) {
log_info("[>%d][%ld] Buffer (0x%zxo) is:", depth, i + 1, linesize);
logm_info(&buf[entry_offset], linesize);
}
#endif
/* go recursive */
if (lds[i].entry_type == eact_ltype_content
&& (err = g1m_parse_eact_cont(handle, &buf[entry_offset], linesize,
depth + 1)))
return (err);
}
/* no error */
return (0);
}
/**
* g1m_parse_eact_cont:
* Parse an E-Activity content.
*
* @arg handle the handle.
* @arg buf the buffer.
* @arg bufsize the buffer length.
* @arg depth the current depth.
* @return the error code (0 if ok).
*/
static int g1m_parse_eact_cont(g1m_t *handle, uint8_t *buf, size_t bufsize,
int depth)
{
(void)depth;
/* read content header */
size_t could_read = min(bufsize, sizeof(struct eact_contentheader));
if (could_read < 9) return (g1m_error_eof);
struct eact_contentheader hd = {};
memcpy(&hd, buf, could_read);
/* log info */
log_info("[>%d] Type is '%.8s'", depth, hd.type);
log_info("[>%d] Name is '%.16s'", depth, hd.name);
/* prepare for next */
if (bufsize == could_read)
return (0);
buf += could_read;
bufsize -= could_read;
/* if type eact, parse eact */
if (!strcmp((char*)hd.type, "@EACT"))
return (g1m_parse_eact_eact(handle, buf, bufsize, depth));
/* otherwise, no error. */
return (0);
}
/**
* g1m_parse_eact:
* Parse an EACT.
*
* Thanks to Julese50/CoiledSpring for his help on e-acts parsing.
*
* @arg handle the handle.
* @arg stream the stream to parse from.
* @return the error code (0 if ok).
*/
int g1m_parse_eact(g1m_t * handle, FILE *stream)
{
/* parse the header */
DREAD(hd, eact_header)
/* correct endianess */
hd.filesize = be32toh(hd.filesize);
hd.setup_area_size = be32toh(hd.setup_area_size);
hd.eact_version = be32toh(hd.eact_version);
hd.os_version = be32toh(hd.os_version);
/* find out the size of the setup area */
log_info("E-Activity version is '%s'.",
hd.eact_version == EACT_G1E ? "g1e" :
hd.eact_version == EACT_G2E ? "g2e" : "g3e");
log_info("Setup area size is 0x%x bytes long.",
hd.setup_area_size);
/* skip the setup area */
SKIP(hd.setup_area_size)
/* get content buffer */
size_t bufsize = hd.filesize - sizeof(struct standard_header)
- sizeof(struct eact_header) - hd.setup_area_size;
uint8_t buf[bufsize];
READ(&buf, bufsize)
/* parse content */
return (g1m_parse_eact_cont(handle, buf, bufsize, 0));
}

84
src/parse/main.c Normal file
View File

@ -0,0 +1,84 @@
/* ************************************************************************** */
/* _____ _ */
/* parse/main.c |_ _|__ _ _| |__ ___ _ _ */
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
/* | | (_) | |_| | | | | __/ |_| | */
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
/* Last updated: 2016/11/02 14:47:49 |___/ */
/* */
/* ************************************************************************** */
#include <libg1m/internals.h>
/**
* g1m_parse:
* Parse a G1M file.
*
* Read the standard header, correct endianness, check magic numbers,
* then read subparts according to the G1M type.
*
* @arg handle the handle.
* @arg stream the stream to parse from.
* @return the error code (0 if ok).
*/
int g1m_parse(g1m_t *handle, FILE *stream)
{
/* get the standard header */
DREAD(hd, standard_header)
/* reverse */
uint8_t *u = (uint8_t*)&hd;
for (unsigned int i = 0; i < sizeof(struct standard_header); i++)
u[i] = ~u[i];
/* print header */
log_info("Raw standard header is:");
logm_info(&hd, sizeof(struct standard_header));
/* correct standard header endianess */
hd.filesize = be32toh(hd.filesize);
hd.number = be16toh(hd.number);
/* what is the expected magic sequence? */
const uint8_t magic_sequence_cg[] = {0x00, 0x01, 0x00, 0x01, 0x00};
const uint8_t magic_sequence_0[] = {0x00, 0x10, 0x00, 0x10, 0x00};
const uint8_t *magic_sequence =
(hd.type == g1m_type_addin_cg) ? magic_sequence_cg : magic_sequence_0;
/* check standard header magics */
if (memcmp(hd.magic, magic_sequence, 5)) {
log_info("Magic sequence isn't right.");
return (g1m_error_magic);
} else if (hd.control != ((hd.filesize + 0x41) & 0xff)) {
log_info("First control byte isn't right.");
return (g1m_error_magic);
} else if (hd.control2 != ((hd.filesize + 0xb8) & 0xff)) {
log_info("Second control byte isn't right.");
return (g1m_error_magic);
}
/* log some data */
log_info("Standard Header Main ID is '%.8s'", hd.main_id);
log_info("Standard Header Type is '%s' (0x%02x)",
g1m_get_type_string(hd.type), hd.type);
log_info("Standard Header filesize is %uo", hd.filesize);
log_info("Standard Header num is %d.", hd.number);
/* subparse. */
switch (hd.type) {
case g1m_type_addin:
return (g1m_parse_addin(handle, stream));
case g1m_type_addin_cg:
return (g1m_parse_addin_cg(handle, stream, &hd));
case g1m_type_g2r: case g1m_type_mcs:
return (g1m_parse_mcs(handle, stream, hd.number));
case g1m_type_eact:
return (g1m_parse_eact(handle, stream));
case g1m_type_g3p:
return (g1m_parse_g3p(handle, stream, &hd));
default: break;
}
/* no error */
return (0);
}

271
src/parse/mcs.c Normal file
View File

@ -0,0 +1,271 @@
/* ************************************************************************** */
/* _____ _ */
/* parse/mcs.c |_ _|__ _ _| |__ ___ _ _ */
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
/* | | (_) | |_| | | | | __/ |_| | */
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
/* Last updated: 2016/11/02 14:47:49 |___/ */
/* */
/* ************************************************************************** */
#include <libg1m/internals.h>
/**
* g1m_parse_mcs_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 g1m_parse_mcs_program(g1m_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);
/* seek for what's left in the file */
SKIP(length - sizeof(struct mcs_programheader))
/* no error */
return (0);
}
/**
* g1m_parse_mcs_spreadsheet:
* Parse a spreadsheet.
*
* @arg handle the handle.
* @arg stream the stream to parse from.
* @return the error code (0 if ok).
*/
static int g1m_parse_mcs_spreadsheet(g1m_t *handle, FILE *stream)
{
/* read header */
DREAD(hd, mcs_spreadsheetheader)
/* correct endianess */
uint_fast32_t colcount = be32toh(hd.column_count << 8);
hd.defs_size = be32toh(hd.defs_size);
/* log some info */
log_info("%ld columns to parse!", colcount);
/* get the column directory */
uint32_t column_directory[colcount];
READ(&column_directory, sizeof(uint32_t) * colcount)
/* - we should use `be32toh` here, but it isn't necessary for bool check. */
/* 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)
/* log it */
log_info("[%ld, %ld] not empty.", c, i);
}
/* iterate bit and rd */
rd += (bit & 1);
bit = (bit >> 1) | ((bit & 1) << 7);
}
}
/* no error */
return (0);
}
/**
* g1m_parse_mcs_list:
* Parse an List.
*
* @arg handle the handle.
* @arg stream the stream to parse from.
* @arg size the
* @return the error code (0 if ok).
*/
static int g1m_parse_mcs_list(g1m_t *handle, FILE *stream, uint_fast32_t size)
{
/* read header */
DREAD(hd, mcs_listheader)
/* correct endianess */
uint16_t elcount = hd.element_count;
elcount = be16toh(elcount);
/* prepare browsing */
size -= sizeof(struct mcs_listheader);
size_t elsize = sizeof(struct bcd) * elcount;
/* make tabs */
struct bcd real[elcount];
struct bcd imgn[elcount];
bzero(real, elsize);
bzero(imgn, elsize);
/* browse */
log_info("Browsing %d list elements", elcount);
READ(&real, elsize)
if (size > elsize) /* FIXME: HACK */
READ(&imgn, elsize)
/* no error */
return (0);
}
/**
* g1m_parse_mcs_mat:
* Parse a Matrix.
*
* @arg handle the handle.
* @arg stream the stream to parse from.
* @return the error code (0 if ok).
*/
static int g1m_parse_mcs_mat(g1m_t *handle, FILE *stream)
{
/* read header */
DREAD(hd, mcs_matheader)
/* correct endianess */
hd.width = be16toh(hd.width);
hd.height = be16toh(hd.height);
/* log info */
log_info("Matrix is %d/%d", hd.width, hd.height);
/* parse cells */
size_t num = hd.width * hd.height;
for (size_t i = 0; i < num; i++) {
/* read the cell */
DREAD(cell, bcd)
/* get x using `i % height` and y using `i / height` */
}
/* no error */
return (0);
}
/**
* g1m_parse_mcs_capture:
* Parse a capture.
*
* @arg handle the handle.
* @arg stream the stream to parse from.
* @return the error code (0 if ok).
*/
static int g1m_parse_mcs_capture(g1m_t *handle, FILE *stream)
{
/* read header */
DREAD(hd, mcs_captureheader)
/* correct endianess */
hd.width = be16toh(hd.width);
hd.height = be16toh(hd.height);
/* print info */
log_info("capture is %ux%u sized", hd.width, hd.height);
/* skip */
SKIP(hd.height * (hd.width / 8 + !!(hd.width % 8)))
/* no error */
return (0);
}
/**
* g1m_parse_mcs:
* We have passed the Standard Header and it's a g... MCS! Let's parse it!
*
* @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, uint_fast16_t num)
{
log_info("%ld parts to browse", num);
/* read all of the parts */
for (uint_fast16_t i = 0; i < num; i++) {
/* get the subheader */
DREAD(hd, mcs_subheader)
/* correct endianess */
hd.subcount = be32toh(hd.subcount);
/* log info about part */
log_info("[%ld] Internal name is '%.16s'", i, hd.intname);
log_info("[%ld] %d subparts to browse", i, hd.subcount);
/* foreach subpart */
uint_fast32_t subcount = hd.subcount;
for (uint_fast32_t j = 0; j < subcount; j++) {
/* get the part header */
DREAD(phd, mcs_partheader)
/* correct endianess */
phd.datalength = be32toh(phd.datalength);
/* log info about the subpart */
log_info("[%ld,%ld] directory name is '%.8s'", i, j, phd.dirname);
log_info("[%ld,%ld] filename is '%.8s'", i, j, phd.filename);
log_info("[%ld,%ld] directory type is '%s' (0x%02d)",
i, j, g1m_get_dtype_string(phd.dirtype), phd.dirtype);
log_info("[%ld,%ld] data length is %u", i, j, phd.datalength);
/* and read that much data */
int err = 0;
switch (phd.dirtype) {
case mcs_dtype_program:
err = g1m_parse_mcs_program(handle, stream, phd.datalength);
break;
case mcs_dtype_spreadsheet:
err = g1m_parse_mcs_spreadsheet(handle, stream);
break;
case mcs_dtype_capture:
err = g1m_parse_mcs_capture(handle, stream);
break;
case mcs_dtype_list:
err = g1m_parse_mcs_list(handle, stream, phd.datalength);
break;
case mcs_dtype_mat:
err = g1m_parse_mcs_mat(handle, stream);
break;
default:
SKIP(phd.datalength)
}
/* if there is an error, return it */
if (err) return (err);
}
}
/* no error */
return (0);
}

60
src/parse/picture_cg.c Normal file
View File

@ -0,0 +1,60 @@
/* ************************************************************************** */
/* _____ _ */
/* parse/picture_cg.c |_ _|__ _ _| |__ ___ _ _ */
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
/* | | (_) | |_| | | | | __/ |_| | */
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
/* Last updated: 2016/11/02 14:47:49 |___/ */
/* */
/* ************************************************************************** */
#include <libg1m/internals.h>
/**
* g1m_parse_g3p:
* Parse a G3P file.
*
* @arg handle the handle.
* @arg stream the stream to parse from.
* @arg std the standard header.
* @return the error code (0 if ok).
*/
int g1m_parse_g3p(g1m_t *handle, FILE *stream,
struct standard_header *std)
{
/* get the header and footer */
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 */
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");
return (g1m_error_magic);
}
/* TODO: what about "length of packed data + 4" control? */
/* TODO: get footer, check control 0100 */
/* check if image is encrypted */
int is_encrypted = (((std->filesize & 0xff) +
((std->filesize & 0xff00) >> 8) + g1m_type_g3p) & 0xff) ^ 0x1c;
/* log info */
log_info("Width: %dpx, height: %dpx", hd.width, hd.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");
/* no error */
return (0);
}

73
src/parse/skip.c Normal file
View File

@ -0,0 +1,73 @@
/* ************************************************************************** */
/* _____ _ */
/* parse/skip.c |_ _|__ _ _| |__ ___ _ _ */
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
/* | | (_) | |_| | | | | __/ |_| | */
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
/* Last updated: 2016/11/02 14:48:51 |___/ */
/* */
/* ************************************************************************** */
#include <libg1m/internals.h>
/**
* g1m_parse_skip:
* Skip some bytes.
*
* I've made two loops to avoid conditions in each loop iteration
* when checksum is not calculated.
*
* @arg stream the stream where to skip bytes.
* @arg size the size to skip.
* @arg checksum pointer to the checksum variable if there is one.
*/
int g1m_parse_skip(FILE *stream, size_t size, uint_fast32_t *checksum)
{
uint8_t buf[1024];
size_t size_ini = size;
if (checksum) {
uint_fast32_t add = 0;
while (1) {
/* if no size left, exit */
if (!size) break;
/* read that much */
{
size_t curlen = min(size, 1024);
size_t read_len = fread(buf, 1, curlen, stream);
size -= read_len;
if (read_len < curlen) {
log_info("SKIPPING failed, skipped %zu/%zu bytes, "
"%zu missing", size_ini - size, size_ini, size);
return (g1m_error_eof);
}
/* feed the checksum */
for (size_t i = 0; i < curlen; i++)
add += buf[i];
}
}
*checksum += add;
} else {
while (1) {
/* if no size left, exit */
if (!size) break;
/* read that much */
{
size_t curlen = min(size, 1024);
size_t read_len = fread(buf, 1, curlen, stream);
size -= read_len;
if (read_len < curlen) {
log_info("SKIPPING failed, skipped %zu/%zu bytes, "
"%zu missing", size_ini - size, size_ini, size);
return (g1m_error_eof);
}
}
}
}
/* no error */
return (0);
}