cake
/
libg1m
Archived
1
0
Fork 0

Initial commit

This commit is contained in:
Thomas Touhey 2016-10-30 20:18:15 +01:00
commit 1cfa9d733b
17 changed files with 1306 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/Makefile.cfg
/obj
/libg1m.so*
/man

206
Makefile Normal file
View File

@ -0,0 +1,206 @@
#!/usr/bin/make -f
#******************************************************************************#
# Include variables and message subsystem #
#******************************************************************************#
include Makefile.vars
include Makefile.msg
#******************************************************************************#
# General targets #
#******************************************************************************#
# Build everything.
all: all-lib all-doc
# Mostly clean everything. (remove everything but the end results)
mostlyclean: mostlyclean-lib mostlyclean-doc
mclean: mostlyclean
# Clean everything.
clean: clean-lib clean-doc
fclean: clean
# Remake everything. (clean and build)
re: clean all
# Install everything.
install: install-lib $(if $(INSTALL_MANPAGES),install-doc)
# Uninstall everything. (EXPERIMENTAL)
uninstall: uninstall-lib uninstall-doc
# Reinstall everything. (EXPERIMENTAL)
reinstall: uninstall install
.PHONY: all mostlyclean mclean clean fclean re install uninstall reinstall
#******************************************************************************#
# Configuration (version) checking dependencies #
#******************************************************************************#
# Define the dependencies.
CHECKCFG := $(if $(shell test -f Makefile.cfg || echo y),check-config, \
$(if $(shell [ "$(VERSION)" = "$(CONFIG_VERSION)" ] || echo y), \
check-config-version))
# Define the rules.
check-config:
@echo -e "\033[1;31mNo configuration file found!"
@echo -e "You should configure before re-running this target.\033[0m"
@false
check-config-version:
@echo -e "\033[1;31mConfiguration version is incorrect!"
@echo -e "You should re-configure before re-running this target.\033[0m"
@false
.PHONY: check-config check-config-version
#******************************************************************************#
# Information getting from the Makefile variables #
#******************************************************************************#
# Get the project name.
getname:
@echo lib$(NAME)
# Get the project author.
getauthor:
@echo $(AUTHOR)
# Get the project author email.
getmail:
@echo $(AUTHOR_MAIL)
# Get the project version.
getversion:
@echo $(VERSION)
.PHONY: getname getauthor getmail getversion
#******************************************************************************#
# Library-specific targets #
#******************************************************************************#
# Make the library.
all-lib: $(CHECKCFG) lib$(NAME).so
# Make the objects directory.
$(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 the (shared) library.
lib$(NAME).so: $(SRC:%=$(OBJDIR)/%.o)
$(call bcmd,ld,$@,$(LD) -o $@ $^ $(LDFLAGS))
# Remove the objects directory.
mostlyclean-lib:
$(call rmsg,Removing the objects directory.)
$(call rcmd,$(OBJDIR),$(RM) -r $(OBJDIR))
mclean-lib: mostlyclean-lib
# Clean and remove the built library
clean-lib: mclean-lib
$(call rmsg,Removing the built library.)
$(call rcmd,lib$(NAME).so,$(RM) lib$(NAME).so)
# Remake the library
re-lib: clean-lib all-lib
# Install the library and development files.
install-lib: $(CHECKCFG) lib$(NAME).so
$(call imsg,Installing the library.)
$(call qcmd,$(INST) -m 755 -d "$(ILIBDIR)")
$(call icmd,lib$(NAME).so.$(VERSION_MAJOR),\
$(INST) -m 755 lib$(NAME).so "$(ILIBDIR)/lib$(NAME).so.$(VERSION_MAJOR)")
$(if $(INSTALL_DEVEL),\
$(call imsg,Linking lib$(NAME).so to lib$(NAME).so.$(VERSION_MAJOR).))
$(if $(INSTALL_DEVEL),\
$(call qcmd,$(LN) lib$(NAME).so.$(VERSION_MAJOR) \
"$(ILIBDIR)/lib$(NAME).so"))
$(if $(INSTALL_DEVEL),\
$(call imsg,Installing development files.))
$(if $(INSTALL_DEVEL),\
$(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))))
# Uninstall the library and development files. (EXPERIMENTAL)
uninstall-lib: $(CHECKCFG)
$(call rmsg,Uninstalling the library.)
$(call rcmd,lib$(NAME).a)
$(call rcmd,lib$(NAME).so.*)
$(call qcmd,$(RM) "$(ILIBDIR)/lib$(NAME).a" "$(ILIBDIR)/lib$(NAME).so"*)
$(call rcmd,include/lib$(NAME).h)
$(call rcmd,include/lib$(NAME))
$(call qcmd,$(RM) "$(IINCDIR)/lib$(NAME).h")
$(call qcmd,$(RM) -r "$(IINCDIR)/lib$(NAME)")
.PHONY: all-lib mostlyclean-lib mclean-lib clean-lib re-lib
.PHONY: install-lib uninstall-lib
#******************************************************************************#
# Documentation-related #
#******************************************************************************#
# Make all manpages.
all-doc: $(foreach s,$(MAN_SECTIONS), $(MAN_$(s):%=$(MANDIR)/man$(s)/%.$(s)))
# Make manpages directories.
$(MAN_SECTIONS:%=$(MANDIR)/man%):
$(call bcmd,mkdir,$@,$(MD) $@)
# Make a manpage.
define make-manpage-rule
$(MANDIR)/man$1/%.$1: $(DOCDIR)/%.$1.txt | $(MANDIR)/man$1
$(call bcmd,a2x,$$<,$(A2X) -f manpage -D $$| $$< 2>/dev/null)
endef
$(foreach section, $(MAN_SECTIONS), \
$(eval $(call make-manpage-rule,$(section))))
# Mostly clean (do nothing, really)
mostlyclean-doc:
mclean-doc: mostlyclean-doc
# Remove all built manpages.
clean-doc:
$(call rmsg,Removing manpages directory.)
$(call rcmd,$(MANDIR),$(RM) -r $(MANDIR))
# Remake all manpages.
# (I don't really know why some people would want to do that though)
re-doc: clean-doc all-doc
# Install a manpages section.
define make-installmansection-rule
install-doc-$1: $(MAN_$1:%=$(MANDIR)/man$1/%.$1)
$(call imsg,Installing manpages section $1.)
$(call qcmd,$(INST) -m 755 -d "$(IMANDIR)/man$1")
$(foreach i,$(MAN_$1),$(call icmd,man$1/$(i).$1))
$(call qcmd,$(INST) -m 644 -t "$(IMANDIR)/man$1" \
$(MAN_$1:%=$(MANDIR)/man$1/%.$1))
$(call qcmd,$(GZIP) $(MAN_$1:%="$(IMANDIR)/man$1/%.$1"))
endef
$(foreach section, $(MAN_SECTIONS), \
$(eval $(call make-installmansection-rule,$(section))))
# Install manpages.
install-doc: $(CHECKCFG) $(MAN_SECTIONS:%=install-doc-%)
# Clean a manpages section.
define make-uninstall-doc-rule
uninstall-doc-$1:
$(call rmsg,Uninstalling manpages section $1.)
$(call rcmd,man$1/lib$(NAME).$1)
$(call rcmd,man$1/$(NAME)_*.$1)
$(call qcmd,$(RM) "$(IMANDIR)/man$1/lib$(NAME).$1"* \
"$(IMANDIR)/man$1/$(NAME)_"*".$1"*)
endef
$(foreach sec,$(MAN_SECTIONS), \
$(eval $(call make-uninstall-doc-rule,$(sec))))
# Uninstall manpages
uninstall-doc: $(CHECKCFG) $(MAN_SECTIONS:%=uninstall-doc-%)
.PHONY: all-doc mostlyclean-doc mclean-doc clean-doc re-doc
.PHONY: install-doc uninstall-doc
.PHONY: $(foreach s,$(MAN_SECTIONS),install-doc-$(s) uninstall-doc-$(s))
# End of file.

66
Makefile.msg Normal file
View File

@ -0,0 +1,66 @@
#!/usr/bin/make -f
# The Makefile message subsystem.
# For nice logs. 5 dollars per log only.
#******************************************************************************#
# Colors and misc #
#******************************************************************************#
# Used colors ANSI modifiers escape codes
color_green := 32
color_red := 31
color_yellow := 33
# Newline - comes handy in some situations
define \n
endef
#******************************************************************************#
# General messages #
#******************************************************************************#
# Command message - display basic info about the command, and run it.
define cmd
@$(if $(MAKE_FULL_LOG),,printf "\033[1;""$4""m>\033[0m \033[1m%s\033[0m %s\n" "$1" "$2";)
$(if $(MAKE_FULL_LOG),,@)$3
endef
# Quiet command - make it non-quiet if full log is enabled.
define qcmd
$(if $(MAKE_FULL_LOG),,@)$1
endef
# Normal message - display it.
define msg
$(if $(MAKE_FULL_LOG),,@printf "\033[1;""$2""m>\033[0m \033[1m%s\033[0m\n" "$1")
endef
#******************************************************************************#
# Commands #
#******************************************************************************#
# Build command
define bcmd
$(call cmd,$1,$2,$3,$(color_green))
endef
# Remove command
define rcmd
$(call cmd,rm,$1,$2,$(color_red))
endef
# Install command
define icmd
$(call cmd,install,$1,$2,$(color_yellow))
endef
#******************************************************************************#
# Messages #
#******************************************************************************#
# Remove message
define rmsg
$(call msg,$1,$(color_red))
endef
# Install message
define imsg
$(call msg,$1,$(color_yellow))
endef

118
Makefile.vars Normal file
View File

@ -0,0 +1,118 @@
#!/usr/bin/make -f
#******************************************************************************#
# Include configuration #
#******************************************************************************#
-include Makefile.cfg
#******************************************************************************#
# Project main information #
#******************************************************************************#
# Project name
NAME := g1m
# Author information
AUTHOR := Thomas \"Cakeisalie5\" Touhey
AUTHOR_MAIL := thomas@touhey.fr
# Project license
LICENSE := GPLv2
# Project version
VERSION_MAJOR := 0
VERSION_MINOR := 1
INDEV := yes
VERSION := $(VERSION_MAJOR).$(VERSION_MINOR)$(if $(INDEV),-indev)
#******************************************************************************#
# Project directories #
#******************************************************************************#
# Headers directory - where all the headers are.
INCDIR := ./include
# Sources directory - where all the sources are.
SRCDIR := ./src
# Objects directory - where the objects will be put.
OBJDIR := ./obj
# Documentation directory - where the asciidoc sources for the manpages are.
DOCDIR := ./doc
# Manpages directory - where the manpages will be put.
MANDIR := ./man
#******************************************************************************#
# Binary utilities #
#******************************************************************************#
# Compiler
CC := gcc
# - Check flags (warnings)
CHKFLAGS := -Wall -Wextra -Wno-attributes
# - For random manipulations (profiling, ...)
CMOREFLAGS :=
# - All C compiling flags
CFLAGS := -I $(INCDIR) $(CHKFLAGS) -std=gnu11 -fPIC -O2 \
-fstack-protector-strong -D LOGLEVEL="ll_$(LOG_LEVEL)" \
-D_GNU_SOURCE -D_USE_GNU \
-D AUTHOR="$(AUTHOR)" -D AUTHOR_MAIL="$(AUTHOR_MAIL)" \
-D LICENSE="$(LICENSE)" -D VERSION="$(VERSION)" \
$(CMOREFLAGS)
# Linker
LD := gcc
# - Linker flags
LDFLAGS := -shared -lusb-1.0 \
-e __lib$(NAME)_version \
-Wl,-soname,lib$(NAME).so.$(VERSION_MAJOR) \
-Wl,-z,relro -Wl,-z,combreloc -Wl,-z,defs
# Directory maker
MD := mkdir -p
# Symbolic link maker
LN := ln -sf
# File remover
RM := rm -f
# Documentation creator
A2X := a2x
# Installer
INST := install
# GZipper
GZIP := gzip -f
#******************************************************************************#
# Look for sources #
#******************************************************************************#
SRC := $(basename $(shell find $(SRCDIR) -name "*.c" -printf "%P\n"))
#******************************************************************************#
# Look for public headers (not internals.h or internals/**/*.h #
#******************************************************************************#
INCPUB := $(basename $(shell find $(INCDIR) \
-name "*.h" -and -not -path "*internals*" -printf "%P\n" | sort))
#******************************************************************************#
# Look for manpages #
#******************************************************************************#
# Get the manpages sections and contents
MAN_SECTIONS :=
define check-man
MAN_SECTIONS += $1
MAN_$1 += $2
endef
$(foreach doc, $(basename $(shell find $(DOCDIR) \
-maxdepth 1 -mindepth 1 -printf "%P\n" -type f -or -type l -name "*.*.txt")), \
$(eval $(call check-man,$(patsubst .%,%,$(suffix $(doc))),$(basename $(doc)))))
# Remove duplicate sections.
MAN_SECTIONS := $(sort $(MAN_SECTIONS))
#******************************************************************************#
# Check for DESTDIR (add as prefix to installation root) #
#******************************************************************************#
define add-dest-dir
$1 = $(DESTDIR)$($1)
endef
$(if $(DESTDIR), $(foreach idir,ILIBDIR IINCDIR IMANDIR UDEVDIR, \
$(eval $(call add-dest-dir,$(idir)))))
# END OF FILE

0
README.md Normal file
View File

140
configure vendored Executable file
View File

@ -0,0 +1,140 @@
#!/bin/sh
# Defaults
name="$(make -sC $(dirname $0) getname)"
author="$(make -sC $(dirname $0) getauthor)"
complete_author="$author <$(make -sC $(dirname $0) getmail)>"
version="$(make -sC $(dirname $0) getversion)"
# - Platform
platform="$(gcc --print-multiarch)"
platform="$([ "$platform" ] && echo "/$platform")"
# - Basic things
make_full_log=
loglevel=none # none, info, warn, error, fatal
root=''
# - main things
prefix='${root}/usr'
bindir='${prefix}/bin'
libdir='${prefix}/lib'"$platform"
includedir='${prefix}/include'"$platform"
mandir='${prefix}/share/man'
# - misc
install_manpages=yes
install_devel=yes
# Help
usage() {
cat <<EOF
\`configure\` configures ${name} to adapt to systems that aren't mine.
Usage: $0 [OPTION]
Defaults for the options are specified in brackets.
General options:
--help display this help and exit
--version display version information and quit
--make-full-log display full commands while making
Build options:
--loglevel library log level [$loglevel]
Installation options:
--noinstall-manpages should not install manpages
--noinstall-devel should not install developement files
Installation directories:
--root=ROOT installation root [$root]
--prefix=PREFIX main installation prefix [$prefix]
Fine tuning of the installation directories:
--bindir=DIR executables [$bindir]
--libdir=DIR library files of the linker [$libdir]
--includedir=DIR include files for the compiler [$includedir]
--mandir=DIR man root [$mandir]
Report bugs to ${complete_author}.
EOF
exit 0
}
# Version
version() {
cat <<EOF
${name} configure script v${version}
Hand-written by ${author}.
This configure script is free software.
There is NO warranty; not even for MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.
EOF
exit 0
}
# Args parsing
# - check for help and version
put_version=
put_help=
for arg ; do case "$arg" in
--help|-h) put_help=1 ;;
--version|-v) put_version=1 ;;
esac; done
if [ $put_version ]; then version; fi
if [ $put_help ]; then usage; fi
# - get options
for arg ; do case "$arg" in
--make-full-log) make_full_log=yes ;;
--loglevel=*)
level="${arg#*=}"
# check if is in array
if ! [ $level = "info" ] && ! [ $level = "warn" ] \
&& ! [ $level = "error" ] && ! [ $level = "fatal" ] \
&& ! [ $level = "none" ]; then
echo \
"$0 : --loglevel: expected one of [info, warn, error, fatal, none], \
got '$level'"
continue
fi
# then set
loglevel=$level ;;
--noinstall-manpages) install_manpages= ;;
--noinstall-devel) install_devel= ;;
--root=*) root="${arg#*=}" ;;
--prefix=*) prefix="${arg#*=}" ;;
--bindir=*) bindir="${arg#*=}" ;;
--libdir=*) libdir="${arg#*=}" ;;
--includedir=*) includedir="${arg#*=}" ;;
--mandir=*) mandir="${arg#*=}" ;;
*) echo "$arg: didn't read" ;;
esac; done
# Evaluate
for var in prefix sysconfdir bindir libdir includedir mandir udevrulesdir; do
eval $var'='$(eval 'echo $'$var)
done
# Create Makefile configuration
exec 3>&1 1>"$(dirname $0)/Makefile.cfg"
cat <<EOF
#!/usr/bin/make -f
# Makefile configuration generated by ./configure
# - Configuration version
CONFIG_VERSION = $version
MAKE_FULL_LOG = $make_full_log
# - Build options
LOG_LEVEL = $loglevel
# - Install options
INSTALL_MANPAGES = $install_manpages
INSTALL_DEVEL = $install_devel
# - Install prefix and directories
IBINDIR = $bindir
ILIBDIR = $libdir
IINCDIR = $includedir
IMANDIR = $mandir
EOF
exec 1>&3 3>&-
chmod +x "$(dirname $0)/Makefile.cfg"
# We're done
echo "Configuration loaded, you can make now."

25
doc/libg1m.3.txt Normal file
View File

@ -0,0 +1,25 @@
LIBG1M(3)
=========
Thomas "Cakeisalie5" Touhey
:Email: thomas@touhey.fr
:man source: libg1m
:man manual: libg1m manual
NAME
----
libg1m - manipulation library for casio's standard format
SYNOPSIS
--------
[source,c]
----
#include <libg1m.h>
----
DESCRIPTION
-----------
libg1m enables reading (parsing) and writing of g** files : g1m, g1a.
SEE ALSO
--------
*g1m_open(3)*

59
include/libg1m.h Normal file
View File

@ -0,0 +1,59 @@
/* ************************************************************************** */
/* _____ _ */
/* libg1m.h |_ _|__ _ _| |__ ___ _ _ */
/* | Project : libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
/* | | (_) | |_| | | | | __/ |_| | */
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
/* Last updated: 2016/09/03 23:23:56 |___/ */
/* */
/* ************************************************************************** */
#ifndef LIBG1M_H
# define LIBG1M_H
# include <stdio.h>
# include <libg1m/compiler.h>
# ifdef __cplusplus
extern "C" {
# endif
/* ************************************************************************** */
/* Errors */
/* ************************************************************************** */
/* main enumeration */
typedef enum {
g1m_noerror,
/* check errno for more info */
g1m_error_errno,
/* no stream sent */
g1m_error_nostream,
/* could not read from stream */
g1m_error_noread,
/* could not seek in stream */
g1m_error_noseek,
/* magic or control problem */
g1m_error_magic,
/* memory allocation problem */
g1m_error_alloc,
} g1m_error_t;
/* Message getting macro */
extern const char *g1m_error_strings[];
#define g1m_strerror(N) g1m_error_strings[N]
#define g1m_geterror(N) g1m_error_strings[N]
/* ************************************************************************** */
/* Main types and functions */
/* ************************************************************************** */
/* handle */
struct g1m;
typedef struct g1m g1m_t;
/* open and close handles */
int g1m_open(g1m_t **handle, const char *path);
int g1m_fopen(g1m_t **handle, FILE *stream);
void g1m_close(g1m_t *handle);
# ifdef __cplusplus
}
# endif
#endif /* LIBG1M_H */

18
include/libg1m/compiler.h Normal file
View File

@ -0,0 +1,18 @@
/* ************************************************************************** */
/* _____ _ */
/* libg1m/compiler.h |_ _|__ _ _| |__ ___ _ _ */
/* | Project : libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
/* | | (_) | |_| | | | | __/ |_| | */
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
/* Last updated: 2016/09/05 13:22:50 |___/ */
/* */
/* ************************************************************************** */
#ifndef LIBG1M_COMPILER_H
# define LIBG1M_COMPILER_H
# ifndef __unused
# define __unused __attribute__((unused))
# endif
#endif /* LIBG1M_COMPILER_H */

View File

@ -0,0 +1,30 @@
/* ************************************************************************** */
/* _____ _ */
/* internals.h |_ _|__ _ _| |__ ___ _ _ */
/* | Project : libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
/* | | (_) | |_| | | | | __/ |_| | */
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
/* Last updated: 2016/09/03 23:24:04 |___/ */
/* */
/* ************************************************************************** */
#ifndef LIBG1M_INTERNALS_H
# define LIBG1M_INTERNALS_H
# include <libg1m.h>
# include <libg1m/internals/log.h>
# include <libg1m/internals/format.h>
/* ************************************************************************** */
/* Main handle type */
/* ************************************************************************** */
/* Handle */
struct g1m {
FILE *stream;
};
/* ************************************************************************** */
/* Utilities */
/* ************************************************************************** */
/* Parsing */
int g1m_parse(g1m_t *handle);
#endif /* LIBG1M_INTERNALS_H */

View File

@ -0,0 +1,155 @@
/* ************************************************************************** */
/* _____ _ */
/* 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 <libg1m/compiler.h>
# include <stdint.h>
/* 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 on its calculators for storing add-ins
* (compiled programs), MCS (main memory) saves, and
* e-acts (simple documents). */
enum g1m_type {
g1m_type_addin = 0xF3,
g1m_type_mcs = 0x31,
g1m_type_eact = 0x49
};
/* 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.
*
* Multi-bytes 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: {0x00, 0x10, 0x00, 0x10, 0x00} */
uint8_t magic[5];
/* first control byte: filesize LSB + 0x41 */
uint8_t control;
/* magic byte: 0x01 */
uint8_t magic2;
/* total filesize */
uint32_t filesize;
/* second control byte: filesize LSB + 0xb8 */
uint8_t control2;
/* 4-bytes alignment */
uint8_t align[9] __unused;
/* 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.
*
* Let's start with the simplest type: the add-in part. Well, it will always
* have one part: the add-in part, with the G1A subheader. */
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.
* But hey, that was the easiest G1M type. Let's dive through the feared
* type: the MCS one.
*
* 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_picture = 0x7,
mcs_dtype_capture = 0xa
};
/* 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. */
uint16_t datalength;
/* some alignment */
uint8_t align[3] __unused;
};
/* And I don't know anything about the E-Act type, sorry. */
#endif /* LIBG1M_INTERNALS_FORMAT_H */

View File

@ -0,0 +1,59 @@
/* ************************************************************************** */
/* _____ _ */
/* libg1m/internals/log.h |_ _|__ _ _| |__ ___ _ _ */
/* | Project : libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
/* | | (_) | |_| | | | | __/ |_| | */
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
/* Last updated: 2016/09/05 12:58:52 |___/ */
/* */
/* ************************************************************************** */
#ifndef LIBG1M_INTERNALS_LOG_H
# define LIBG1M_INTERNALS_LOG_H
/* ************************************************************************** */
/* Log utility */
/* ************************************************************************** */
/* Log level */
# define ll_info 0
# define ll_warn 1
# define ll_error 2
# define ll_fatal 3
# define ll_none 4
/* Macros */
# define log(P, S, ...) fprintf(stderr, P "%s: " S "\n", __FUNCTION__, ##__VA_ARGS__)
# define logm(S, M, N) g1m_log_mem(S, M, N)
# if LOGLEVEL <= ll_info
# define log_info(S, ...) log("[libg1m info] ", S, ##__VA_ARGS__)
# define logm_info(M, N) logm("[libg1m info] ", M, N)
# else
# define log_info(S, ...)
# define logm_info(M, N)
# endif
# if LOGLEVEL <= ll_warn
# define log_warn(S, ...) log("[libg1m warn] ", S, ##__VA_ARGS__)
# define logm_warn(M, N) logm("[libg1m warn] ", M, N)
# else
# define log_warn(S, ...)
# define logm_warn(M, N)
# endif
# if LOGLEVEL <= ll_error
# define log_error(S, ...) log("[libg1m error] ", S, ##__VA_ARGS__)
# else
# define log_error(S, ...)
# endif
# if LOGLEVEL <= ll_fatal
# define log_fatal(S, ...) log("[libg1m fatal] ", S, ##__VA_ARGS__)
# else
# define log_fatal(S, ...)
# endif
/* Functions prototypes */
void g1m_log_mem(const char *prefix, void *m, size_t n);
#endif /* LIBG1M_INTERNALS_LOG_H */

85
src/handle.c Normal file
View File

@ -0,0 +1,85 @@
/* ************************************************************************** */
/* _____ _ */
/* handle.c |_ _|__ _ _| |__ ___ _ _ */
/* | Project : libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
/* | | (_) | |_| | | | | __/ |_| | */
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
/* Last updated: 2016/09/04 00:09:47 |___/ */
/* */
/* ************************************************************************** */
#include <libg1m/internals.h>
#include <stdio_ext.h>
#include <stdlib.h>
/**
* g1m_fopen:
* Open handle using FILE* pointer.
*
* @arg handle the handle to create
* @arg stream the stream
* @return the error code (0 if ok)
*/
int g1m_fopen(g1m_t **handle, FILE *stream)
{
int err;
/* check stream */
if (!stream) return (g1m_error_nostream);
if (!__freadable(stream)) return (g1m_error_noread);
/* seek to beginning */
if (fseek(stream, 0L, SEEK_SET)) return (g1m_error_noseek);
/* create the handle */
*handle = malloc(sizeof(g1m_t));
if (!*handle) return (g1m_error_alloc);
(*handle)->stream = stream;
/* fill it by parsing opened file */
if ((err = g1m_parse(*handle))) {
free(*handle);
*handle = NULL;
return (err);
}
/* everything ok */
return (0);
}
/**
* g1m_open:
* Open handle using path.
*
* @arg handle the handle to create.
* @arg path the path of the file to open.
* @return the error code (0 if ok).
*/
int g1m_open(g1m_t **handle, const char *path)
{
/* open stream */
FILE *f = fopen(path, "r+");
if (!f) return (g1m_error_errno);
/* open using `g1m_fopen` */
int err;
if ((err = g1m_fopen(handle, f)))
fclose(f);
return (err);
}
/**
* g1m_close:
* Close handle using path.
*
* @arg handle the handle to close.
* @return the error code (0 if ok).
*/
void g1m_close(g1m_t *handle)
{
/* close stream */
fclose(handle->stream);
free(handle);
}

120
src/log.c Normal file
View File

@ -0,0 +1,120 @@
/* ************************************************************************** */
/* _____ _ */
/* log.c |_ _|__ _ _| |__ ___ _ _ */
/* | Project : libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
/* | | (_) | |_| | | | | __/ |_| | */
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
/* Last updated: 2016/08/19 12:04:33 |___/ */
/* */
/* ************************************************************************** */
#include <libg1m/internals.h>
#include <string.h>
#include <ctype.h>
#define min(A, B) ((A) < (B) ? (A) : (B))
/**
* putascii:
* Put a number in ASCII-hex, in a n-dimensionned field.
*
* @arg p pointer where to put ASCII number
* @arg i ASCII number
* @arg n the size of the field
*/
static void putascii(unsigned char *p, unsigned int i, int n)
{
/* goto end of the field */
p += (n - 1);
/* for each digit */
while (n--) {
/* get end digit from this point */
int j = i % 16;
/* put it in ASCII-hex */
*p-- = j >= 10 ? j - 10 + 'A' : j + '0';
/* then go to digit that's left */
i /= 16;
}
}
/**
* log_mem_hex:
* Prints the octal interpretation of a max of two octets.
*
* @arg s the string where to put it
* @arg m the memory zone to print
* @arg n the size of the memory zone
*/
static void log_mem_hex(char *s, unsigned char *m, size_t n)
{
size_t l = 0;
while (l < 16) {
/* put the hex number */
if (n) putascii((unsigned char*)s, *m++, 2), s += 2;
else *s++ = ' ', *s++ = ' ';
/* decrement size of the memory zone to go */
n -= !!n;
/* go to next character if s is at the ending of a group */
if (l++ % 2) s++;
}
}
/**
* log_mem_asc:
* Prints the ascii interpretation of a max of two octets.
*
* @arg s the string where to put it
* @arg m the memory zone to print
* @arg n the size of the memory zone
*/
static void log_mem_asc(char *s, unsigned char *m, size_t n)
{
size_t l = 0;
/* for each byte */
while (n-- && l++ < 16) {
/* put the character (or a dot if non printable) */
if (isprint(*m++)) *s++ = *((char*)m - 1);
else *s++ = '.';
}
/* put the line ending */
*s++ = '\n', *s = '\0';
}
/**
* g1m_log_mem:
* Print memory zone.
*
* @arg prefx the line prefix
* @arg m the memory zone to print
* @arg n the size of the memory zone
*/
void g1m_log_mem(const char *prefx, void *m, size_t n)
{
/* if nothing, print it directly */
if (!n) fprintf(stderr, "%s(nothing)\n", prefx);
/* prepare line buffer */
unsigned int lineoff = strlen(prefx);
char linebuf[strlen(prefx) + 58];
memcpy(linebuf, prefx, lineoff);
/* - for spaces - */
memcpy(&linebuf[lineoff], "0000 0000 0000 0000 0000 0000 0000 0000 ", 40);
/* then loop-loop-loop-loop-loop */
unsigned char *p = m;
while (n > 0) {
/* fill in ascii-hex part */
log_mem_hex(&linebuf[lineoff], p, n);
/* fill in ascii part */
log_mem_asc(&linebuf[lineoff + 40], p, n);
/* then print line */
fputs(linebuf, stderr);
/* and increment pointer */
p += 16;
n -= min(16, n);
}
}

127
src/parse.c Normal file
View File

@ -0,0 +1,127 @@
/* ************************************************************************** */
/* _____ _ */
/* 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>
/**
* g1m_parse_addin:
* We have passed the Standard Header and it's a b... add-in! Let's parse it!
*
* @arg handle the handle.
* @return the error code (0 if ok).
*/
static int g1m_parse_addin(g1m_t *handle)
{
/* get the subheader */
struct g1a_subheader hd;
size_t rc = fread(&hd, sizeof(struct g1a_subheader), 1, handle->stream);
if (rc < sizeof(struct g1a_subheader)) return (g1m_error_magic);
/* correct subheader endianess */
hd.filesize = be32toh(hd.filesize);
/* no errors */
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 num number of sizes.
* @return the error code (0 if ok).
*/
static int g1m_parse_mcs(g1m_t *handle, uint_fast16_t num)
{
/* read all of the parts */
for (uint_fast16_t i = 0; i < num; i++) {
/* get the subheader */
struct mcs_subheader hd;
size_t rc = fread(&hd, sizeof(struct mcs_subheader), 1, handle->stream);
if (rc < sizeof(struct mcs_subheader)) return (g1m_error_magic);
/* correct endianess */
hd.subcount = be32toh(hd.subcount);
/* foreach subpart */
for (uint_fast32_t j = 0; j < num; j++) {
/* get the part header */
struct mcs_partheader phd;
rc = fread(&phd, sizeof(struct mcs_partheader), 1, handle->stream);
if (rc < sizeof(struct mcs_partheader)) return (g1m_error_magic);
/* correct endianess */
phd.datalength = be32toh(phd.datalength);
/* and read that much data */
if (fseek(handle->stream, phd.datalength, SEEK_CUR))
return (g1m_error_magic);
}
}
/* no error */
return (0);
}
/**
* 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.
* @return the error code (0 if ok).
*/
int g1m_parse(g1m_t *handle)
{
/* get the standard header */
struct standard_header hd;
size_t rc = fread(&hd, sizeof(struct standard_header), 1, handle->stream);
if (rc < sizeof(struct standard_header)) return (g1m_error_magic);
/* correct standard header endianess */
hd.filesize = be32toh(hd.filesize);
hd.number = be16toh(hd.number);
/* check standard header magics */
if (memcmp(hd.magic, (unsigned char[]){0x00, 0x10, 0x00, 0x10, 0x00}, 5))
return (g1m_error_magic);
if (hd.magic2 != 0x01)
return (g1m_error_magic);
if (hd.control != ((hd.filesize + 0x41) & 0xff))
return (g1m_error_magic);
if (hd.control2 != ((hd.filesize + 0xb8) & 0xff))
return (g1m_error_magic);
/* log some data */
log_info("Standard header was the following:");
logm_info(&hd, sizeof(struct standard_header));
log_info("G1M type was '%8s'", hd.main_id);
log_info("Type was 0x%02x", hd.type);
/* subparse. */
switch (hd.type) {
case g1m_type_addin:
return (g1m_parse_addin(handle));
case g1m_type_mcs:
return (g1m_parse_mcs(handle, hd.number));
/* TODO: add g1m_type_eact */
default: break;
}
/* no error */
return (0);
}

37
src/strerror.c Normal file
View File

@ -0,0 +1,37 @@
/* ************************************************************************** */
/* _____ _ */
/* strerror.c |_ _|__ _ _| |__ ___ _ _ */
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
/* | | (_) | |_| | | | | __/ |_| | */
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
/* Last updated: 2016/10/30 20:05:20 |___/ */
/* */
/* ************************************************************************** */
#include <libg1m/internals.h>
/**
* g1m_error_strings:
* String descriptions of libg1m errors.
*
* This list MUST evolve whenever the g1m_error_t enumeration
* is updated. Could cause invalid error strings otherwise,
* or segfaults!
*/
const char *g1m_error_strings[] = {
[g1m_noerror] =
"no error have been encountered",
[g1m_error_errno] =
"libc error, check errno for more info",
[g1m_error_nostream] =
"sent stream was NULL",
[g1m_error_noread] =
"given stream was not readable",
[g1m_error_noseek] =
"given stream was not seekable",
[g1m_error_magic] =
"magic/control/size problem",
[g1m_error_alloc] =
"could not allocate memory",
};

57
src/version.c Normal file
View File

@ -0,0 +1,57 @@
/* ************************************************************************** */
/* _____ _ */
/* version.c |_ _|__ _ _| |__ ___ _ _ */
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
/* | | (_) | |_| | | | | __/ |_| | */
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
/* Last updated: 2016/10/13 07:33:17 |___/ */
/* */
/* ************************************************************************** */
#include <libg1m/internals.h>
#include <unistd.h>
#define Q(x) #x
#define QUOTE(x) Q(x)
/* get loglevel string */
#if LOGLEVEL == ll_info
# define LLS "info"
#elif LOGLEVEL == ll_warn
# define LLS "warn"
#elif LOGLEVEL == ll_error
# define LLS "error"
#elif LOGLEVEL == ll_fatal
# define LLS "fatal"
#elif LOGLEVEL == ll_none
# define LLS "none"
#else
# define LLS "unknown"
#endif
/**
* version_message:
* The message that should be displayed when the library is executed.
*/
static const char version_message[] =
"libg1m v" QUOTE(VERSION) " (licensed under " QUOTE(LICENSE) ")\n"
"Made by " QUOTE(AUTHOR) " <" QUOTE(AUTHOR_MAIL) ">.\n"
"\n"
"Compiled with the '" LLS "' loglevel.\n"
"\n"
"This is free software; see the source for copying conditions.\n"
"There is NO warranty; not even for MERCHANTABILITY or\n"
"FITNESS FOR A PARTICULAR PURPOSE.";
/**
* __libg1m_version:
* Display version when the library is executed.
*/
extern void __libg1m_version(void)
__attribute__((noreturn));
void __libg1m_version(void)
{
puts(version_message);
_exit(0);
}