Working on babel/library support
authorMarijn van Vliet <w.m.vanvliet@gmail.com>
Sun, 19 Jun 2011 10:20:51 +0000 (12:20 +0200)
committerMarijn van Vliet <w.m.vanvliet@gmail.com>
Sun, 19 Jun 2011 10:20:51 +0000 (12:20 +0200)
46 files changed:
Makefile.am
babel/MANIFEST [new file with mode: 0644]
babel/Makefile.am [new file with mode: 0644]
babel/README [new file with mode: 0644]
babel/adrift.c [new file with mode: 0644]
babel/advsys.c [new file with mode: 0644]
babel/agt.c [new file with mode: 0644]
babel/alan.c [new file with mode: 0644]
babel/babel [new file with mode: 0644]
babel/babel-makefile [new file with mode: 0644]
babel/babel.a [new file with mode: 0644]
babel/babel.c [new file with mode: 0644]
babel/babel.h [new file with mode: 0644]
babel/babel_functions.a [new file with mode: 0644]
babel/babel_handler.c [new file with mode: 0644]
babel/babel_handler.h [new file with mode: 0644]
babel/babel_ifiction_functions.c [new file with mode: 0644]
babel/babel_multi_functions.c [new file with mode: 0644]
babel/babel_story_functions.c [new file with mode: 0644]
babel/blorb.c [new file with mode: 0644]
babel/executable.c [new file with mode: 0644]
babel/glulx.c [new file with mode: 0644]
babel/hugo.c [new file with mode: 0644]
babel/ifiction.a [new file with mode: 0644]
babel/ifiction.c [new file with mode: 0644]
babel/ifiction.h [new file with mode: 0644]
babel/level9.c [new file with mode: 0644]
babel/magscrolls.c [new file with mode: 0644]
babel/md5.c [new file with mode: 0644]
babel/md5.h [new file with mode: 0644]
babel/misc.c [new file with mode: 0644]
babel/modules.h [new file with mode: 0644]
babel/modules.h.gch [new file with mode: 0644]
babel/register.c [new file with mode: 0644]
babel/register_ifiction.c [new file with mode: 0644]
babel/tads.c [new file with mode: 0644]
babel/tads.h [new file with mode: 0644]
babel/tads2.c [new file with mode: 0644]
babel/tads3.c [new file with mode: 0644]
babel/treaty.h [new file with mode: 0644]
babel/treaty_builder.h [new file with mode: 0644]
babel/zcode.c [new file with mode: 0644]
configure.ac
tests/Aotearoa.gblorb [new file with mode: 0644]
tests/Makefile.am
tests/babeltest.c [new file with mode: 0644]

index 352b3d352b03ad7d247d845bb0fa70de2952656a..d78eef1f53c04592ae4650f91bdb8eceec5c09b9 100644 (file)
@@ -4,7 +4,7 @@
 if TARGET_ILIAD
 SUBDIRS = libchimara interpreters player po
 else
-SUBDIRS = libchimara interpreters player tests docs po
+SUBDIRS = libchimara interpreters player tests docs po babel
 endif
 
 chimaradocdir = $(datadir)/doc/chimara
diff --git a/babel/MANIFEST b/babel/MANIFEST
new file mode 100644 (file)
index 0000000..09048bd
--- /dev/null
@@ -0,0 +1,51 @@
+adrift.c                        Treaty of Babel module for Adrift\r
+advsys.c                        Treaty of Babel module for AdvSys\r
+alan.c                          Treaty of Babel module for Alan\r
+agt.c                           Treaty of Babel module for AGT\r
+babel.h                         babel program header\r
+md5.h                           L. Peter Deutsch's md5 header\r
+modules.h                       babel module registry\r
+treaty.h                        Treaty of Babel header\r
+treaty_builder.h                Macros to build treaty modules\r
+babel.c                         Babel main program\r
+babel_handler.h                 Babel handler header file\r
+babel_handler.c                 The babel handler api\r
+babel_ifiction_functions.c      Babel program-specific ifiction operations\r
+babel_multi_functions.c         Babel program-specific multi operations\r
+babel_story_functions.c         Babel program-specific story operations\r
+blorb.c                         babel handler blorb module\r
+executable.c                    Treaty of Bable module for executables \r
+glulx.c                         Treaty of Babel module for glulx\r
+hugo.c                          Draft Treaty of Babel module for hugo\r
+ifiction.h                      babel ifiction header\r
+ifiction.c                      babel ifiction api\r
+level9.c                        Treaty of Babel module for level9\r
+magscrolls.c                    Magnetic Scrolls treaty module\r
+makefile                        Provisional makefile\r
+md5.c                           L. Peter Deutsch's md5 implementation\r
+misc.c                          babel memory allocator\r
+register.c                      babel module registry\r
+register_ifiction.c             babel module registry for ifiction API\r
+tads.h                          Prototypes for tads2.c and tads3.c\r
+tads.c                          Common functions for TADS modules\r
+tads2.c                         Treaty of Babel module for tads2\r
+tads3.c                         Treaty of Babel module for tads3\r
+zcode.c                         Treaty of Babel module for zcode\r
+README                          documentation\r
+MANIFEST                        this file\r
+extras/babel-cache.pl           Perl demo of babel interaction\r
+extras/babel-infocom.pl         Special bundler for the infocom corpus\r
+extras/babel-list.c             Babel API demo\r
+extras/babel-marry.pl           Perl simple blorb encapsulator\r
+extras/babel-wed.pl             Perl single file blorb encapsulator\r
+extras/hotload.c                Dynamic loader replacement for register.c\r
+extras/hotload.h                Header file for hotload.c\r
+extras/ifiction-aggregate.c     Utility to combine multiple ifiction files\r
+extras/ifiction-xtract.c        Ifiction API demo\r
+extras/simple-marry.c           Simplified C version of babel-marry\r
+babel-get/babel-get.c           The babel-get application\r
+babel-get/get_dir.c             Directory source\r
+babel-get/get_ifiction.c        ifiction source\r
+babel-get/get_story.c           story file source\r
+babel-get/get_url.c             URL source\r
+babel-get/makefile              Makefile for babel-get\r
diff --git a/babel/Makefile.am b/babel/Makefile.am
new file mode 100644 (file)
index 0000000..1e0d78d
--- /dev/null
@@ -0,0 +1,36 @@
+noinst_LTLIBRARIES = libbabel.la libifiction.la libbabel_functions.la
+
+libbabel_la_SOURCES = babel_handler.c \
+                                  register.c \
+                                  misc.c \
+                                  md5.c \
+                                  zcode.c \
+                                  magscrolls.c \
+                                  blorb.c \
+                                  glulx.c \
+                                  hugo.c \
+                                  agt.c \
+                                  level9.c \
+                                  executable.c \
+                                  advsys.c \
+                                  tads.c \
+                                  tads2.c \
+                                  tads3.c \
+                                  adrift.c \
+                                  alan.c \
+                                  babel.h \
+                                  babel_handler.h \
+                                  md5.h \
+                   modules.h \
+                                  tads.h \
+                                  treaty_builder.h \
+                                  treaty.h
+
+libifiction_la_SOURCES = ifiction.c ifiction.h \
+                                         register_ifiction.c
+
+libbabel_functions_la_SOURCES = babel_story_functions.c \
+                                                        babel_ifiction_functions.c \
+                                                        babel_multi_functions.c
+
+-include $(top_srcdir)/git.mk
diff --git a/babel/README b/babel/README
new file mode 100644 (file)
index 0000000..b754cab
--- /dev/null
@@ -0,0 +1,74 @@
+Version 0.2b, Treaty of Babel Revision 7\r
+This is the source code for babel, the Treaty of Babel analysis tool.\r
+\r
+Most of this code is (c) 2006 by L. Ross Raszewski\r
+\r
+The following files are public domain:\r
+zcode.c\r
+glulx.c\r
+executable.c\r
+level9.c\r
+magscrolls.c\r
+agt.c\r
+hugo.c\r
+advsys.c\r
+misc.c\r
+alan.c\r
+adrift.c\r
+treaty.h\r
+treaty_builder.h\r
+\r
+The following files are Copyright (C) 1999, 2000, 2002 Aladdin Enterprises:\r
+md5.c\r
+md5.h\r
+\r
+And are used in accordance with their licenses.\r
+\r
+All other files are (c) 2006 by L. Ross Raszewski and are released under\r
+the Creative Commons Attribution2.5 License.\r
+\r
+To view a copy of this license, visit\r
+http://creativecommons.org/licenses/by/2.5/ or send a letter to\r
+\r
+Creative Commons,\r
+543 Howard Street, 5th Floor,\r
+San Francisco, California, 94105, USA.\r
+\r
+\r
+To build babel:\r
+\r
+1. compile all the source files in this directory\r
+2. link them together\r
+3. the end\r
+\r
+For folks who find makefiles more useful than generalizations, there is a\r
+makefile provided for babel.  The makefile is currently configured for\r
+Borland's 32-bit C compiler.  Comment out those lines and uncomment the block\r
+which follows for gcc.\r
+\r
+To compile babel-get, first compile babel, then do the same thing in the\r
+babel-get directory.\r
+\r
+To compile ifiction-aggregate, ifiction-xtract, babel-list, and simple-marry,\r
+first compile babel, then compile the relevant C file in the extras/ directory\r
+(These may rely on #include files from the babel directory, so, for example,\r
+to compile ifiction-aggregate, "gcc -c -I.. ifiction-aggregate.c"), then link the\r
+opbject file to the babel and ifiction libraries (babel.lib and ifiction.lib\r
+under Windows, babel.a and ifiction.a most everywhere else.  eg.\r
+"gcc -o ifiction-aggregate ifiction-aggregate.o ../babel.a ../ifiction.a")\r
+\r
+Babel is intended to accept contributions in the form of treaty modules\r
+as defined by the treaty of babel section 2.3.2.\r
+\r
+These modules should use the declarations made in treaty.h.\r
+The file treaty_builder.h generates a generic framework which simplifies\r
+the task of writing treaty modules.  Its use is not required for treaty\r
+compliance, but it should prove useful.\r
+\r
+Parts of babel are intended for use in other programs.  When adapting\r
+babel's source, the files babel.c, babel_story_functions.c and\r
+babel_ifiction_functions.c will probably not prove useful.  However, you\r
+may wish to use babel_handler, which provides a framework for loading a\r
+story file, selecting the proper treaty modules, and seamlessly handling\r
+blorb-wrapped files.\r
+\r
diff --git a/babel/adrift.c b/babel/adrift.c
new file mode 100644 (file)
index 0000000..3718d6a
--- /dev/null
@@ -0,0 +1,89 @@
+/* adrift.c  Treaty of Babel module for Adrift files\r
+ *\r
+ * PROVISIONAL - Hold for someone else\r
+ *\r
+ * This file depends on treaty_builder.h\r
+ *\r
+ * This file is public domain, but note that any changes to this file\r
+ * may render it noncompliant with the Treaty of Babel\r
+ */\r
+\r
+#define FORMAT adrift\r
+#define HOME_PAGE "http://www.adrift.org.uk"\r
+#define FORMAT_EXT ".taf"\r
+#define NO_METADATA\r
+#define NO_COVER\r
+\r
+#include "treaty_builder.h"\r
+\r
+#include <stdio.h>\r
+#include <limits.h>\r
+#include <stdlib.h>\r
+\r
+/* VB RNG constants */\r
+#define  VB_RAND1      0x43FD43FD\r
+#define  VB_RAND2      0x00C39EC3\r
+#define  VB_RAND3      0x00FFFFFF\r
+#define  VB_INIT       0x00A09E86\r
+static int32 vbr_state;\r
+\r
+/*\r
+  Unobfuscates one byte from a taf file. This should be called on each byte\r
+  in order, as the ADRIFT obfuscation function is stately.\r
+\r
+  The de-obfuscation algorithm works by xoring the byte with the next\r
+  byte in the sequence produced by the Visual Basic pseudorandom number\r
+  generator, which is simulated here.\r
+*/\r
+static unsigned char taf_translate (unsigned char c)\r
+{\r
+ int32 r;\r
+\r
+ vbr_state = (vbr_state*VB_RAND1+VB_RAND2) & VB_RAND3;\r
+ r=UCHAR_MAX * (unsigned) vbr_state;\r
+ r/=((unsigned) VB_RAND3)+1;\r
+ return r^c;\r
+}\r
+\r
+static int32 get_story_file_IFID(void *story_file, int32 extent, char *output, int32 output_extent)\r
+{\r
+ int adv;\r
+ unsigned char buf[4];\r
+ unsigned char *sf=(unsigned char *)story_file;\r
+ vbr_state=VB_INIT;\r
+\r
+ if (extent <12) return INVALID_STORY_FILE_RV;\r
+\r
+ buf[3]=0;\r
+ /* Burn the first 8 bytes of translation */\r
+ for(adv=0;adv<8;adv++) taf_translate(0);\r
+ /* Bytes 8-11 contain the Adrift version number in the formay N.NN */\r
+ buf[0]=taf_translate(sf[8]);\r
+ taf_translate(0);\r
+ buf[1]=taf_translate(sf[10]);\r
+ buf[2]=taf_translate(sf[11]);\r
+ adv=atoi((char *) buf);\r
+ ASSERT_OUTPUT_SIZE(12);\r
+ sprintf(output,"ADRIFT-%03d-",adv);\r
+ return INCOMPLETE_REPLY_RV;\r
+\r
+}\r
+\r
+/* The claim algorithm for ADRIFT is to unobfuscate the first\r
+   seven bytes, and check for the word "Version".\r
+   It seems fairly unlikely that the obfuscated form of that\r
+   word would occur in the wild\r
+*/\r
+static int32 claim_story_file(void *story_file, int32 extent)\r
+{\r
+ unsigned char buf[8];\r
+ int i;\r
+ unsigned char *sf=(unsigned char *)story_file;\r
+ buf[7]=0;\r
+ vbr_state=VB_INIT;\r
+ if (extent<12) return INVALID_STORY_FILE_RV;\r
+ for(i=0;i<7;i++) buf[i]=taf_translate(sf[i]);\r
+ if (strcmp((char *)buf,"Version")) return INVALID_STORY_FILE_RV;\r
+ return VALID_STORY_FILE_RV;\r
+\r
+}\r
diff --git a/babel/advsys.c b/babel/advsys.c
new file mode 100644 (file)
index 0000000..e0e1cf8
--- /dev/null
@@ -0,0 +1,49 @@
+/* advsys.c  Treaty of Babel module for AdvSys files\r
+ * 2006 By L. Ross Raszewski\r
+ *\r
+ * This file depends on treaty_builder.h\r
+ *\r
+ * This file is public domain, but note that any changes to this file\r
+ * may render it noncompliant with the Treaty of Babel\r
+ */\r
+\r
+#define FORMAT advsys\r
+#define HOME_PAGE "http://www.ifarchive.org/if-archive/programming/advsys/"\r
+#define FORMAT_EXT ".dat"\r
+#define NO_METADATA\r
+#define NO_COVER\r
+\r
+#include "treaty_builder.h"\r
+#include <ctype.h>\r
+#include <stdio.h>\r
+\r
+/* IFIDs for AdvSys are formed by prepending ADVSYS- to the default\r
+   MD5 ifid\r
+*/\r
+static int32 get_story_file_IFID(void *story_file, int32 extent, char *output, int32 output_extent)\r
+{\r
+ /* This line suppresses a warning from the borland compiler */\r
+ if (story_file || extent) { }\r
+ ASSERT_OUTPUT_SIZE(8);\r
+ strcpy(output,"ADVSYS-");\r
+ return INCOMPLETE_REPLY_RV;\r
+\r
+}\r
+\r
+/* The Advsys claim algorithm: bytes 2-8 of the file contain the\r
+   text "ADVSYS", unobfuscated in the following way:\r
+     30 is added to each byte, then the bits are reversed\r
+*/\r
+static int32 claim_story_file(void *story_file, int32 extent)\r
+{\r
+ char buf[7];\r
+ int i;\r
+ if (extent >=8)\r
+ { \r
+   for(i=0;i<6;i++)\r
+    buf[i]=~(((char *)story_file)[i+2]+30);\r
+   buf[6]=0;\r
+   if (strcmp(buf,"ADVSYS")==0) return VALID_STORY_FILE_RV;\r
+ }\r
+ return INVALID_STORY_FILE_RV;\r
+}\r
diff --git a/babel/agt.c b/babel/agt.c
new file mode 100644 (file)
index 0000000..66b6575
--- /dev/null
@@ -0,0 +1,59 @@
+/* agt.c  Treaty of Babel module for AGX-encapsulated AGT files\r
+ * 2006 By L. Ross Raszewski\r
+ *\r
+ * This file depends on treaty_builder.h\r
+ *\r
+ * This file is public domain, but note that any changes to this file\r
+ * may render it noncompliant with the Treaty of Babel\r
+ */\r
+\r
+#define FORMAT agt\r
+#define HOME_PAGE "http://www.ifarchive.org/indexes/if-archiveXprogrammingXagt"\r
+#define FORMAT_EXT ".agx"\r
+#define NO_METADATA\r
+#define NO_COVER\r
+\r
+#include "treaty_builder.h"\r
+#include <ctype.h>\r
+#include <stdio.h>\r
+\r
+\r
+static char AGX_MAGIC[4] = { 0x58, 0xC7, 0xC1, 0x51 };\r
+\r
+/* Helper functions to unencode integers from AGT source */\r
+static int32 read_agt_short(unsigned char *sf)\r
+{\r
+ return sf[0] | (int32) sf[1]<<8;\r
+}\r
+static int32 read_agt_int(unsigned char *sf)\r
+{\r
+ return (read_agt_short(sf+2) << 16) | read_agt_short(sf);\r
+\r
+}\r
+\r
+static int32 get_story_file_IFID(void *story_file, int32 extent, char *output, int32 output_extent)\r
+{\r
+ int32 l, game_version, game_sig;\r
+ unsigned char *sf=(unsigned char *)story_file;\r
+\r
+ /* Read the position of the game desciption block */\r
+ l=read_agt_int(sf+32);\r
+ if (extent<l+6) return INVALID_STORY_FILE_RV;\r
+ game_version = read_agt_short(sf+l);\r
+ game_sig=read_agt_int(sf+l+2);\r
+ ASSERT_OUTPUT_SIZE(19);\r
+ sprintf(output,"AGT-%05d-%08X",game_version,game_sig);\r
+ return 1;\r
+}\r
+\r
+/* The claim algorithm for AGT is to check for the magic word\r
+   defined above\r
+*/\r
+static int32 claim_story_file(void *story_file, int32 extent)\r
+{\r
+\r
+\r
+ if (extent<36 || memcmp(story_file,AGX_MAGIC,4)) return INVALID_STORY_FILE_RV;\r
+ return VALID_STORY_FILE_RV;\r
+\r
+}\r
diff --git a/babel/alan.c b/babel/alan.c
new file mode 100644 (file)
index 0000000..fb57dbe
--- /dev/null
@@ -0,0 +1,69 @@
+/* alan.c  Treaty of Babel module for ALAN files\r
+ * 2006 By L. Ross Raszewski\r
+ *\r
+ * This file depends on treaty_builder.h\r
+ *\r
+ * This file is public domain, but note that any changes to this file\r
+ * may render it noncompliant with the Treaty of Babel\r
+ */\r
+\r
+#define FORMAT alan\r
+#define HOME_PAGE "http://www.alanif.se/"\r
+#define FORMAT_EXT ".acd"\r
+#define NO_METADATA\r
+#define NO_COVER\r
+\r
+#include "treaty_builder.h"\r
+#include <ctype.h>\r
+#include <stdio.h>\r
+\r
+static int32 read_alan_int(unsigned char *from)\r
+{\r
+ return ((unsigned long int) from[3])| ((unsigned long int)from[2] << 8) |\r
+       ((unsigned long int) from[1]<<16)| ((unsigned long int)from[0] << 24);\r
+}\r
+static int32 get_story_file_IFID(void *story_file, int32 extent, char *output, int32 output_extent)\r
+{\r
+\r
+ if (story_file || extent) { }\r
+ ASSERT_OUTPUT_SIZE(6);\r
+ strcpy(output,"ALAN-");\r
+ return INCOMPLETE_REPLY_RV;\r
+}\r
+/*\r
+  The claim algorithm for Alan files is:\r
+   * For Alan 3, check for the magic word\r
+   * load the file length in blocks\r
+   * check that the file length is correct\r
+   * For alan 2, each word between byte address 24 and 81 is a\r
+      word address within the file, so check that they're all within\r
+      the file\r
+   * Locate the checksum and verify that it is correct\r
+*/\r
+static int32 claim_story_file(void *story_file, int32 extent)\r
+{\r
+ unsigned char *sf = (unsigned char *) story_file;\r
+ int32 bf, i, crc=0;\r
+ if (extent < 160) return INVALID_STORY_FILE_RV;\r
+ if (memcmp(sf,"ALAN",4))\r
+ { /* Identify Alan 2.x */\r
+ bf=read_alan_int(sf+4);\r
+ if (bf > extent/4) return INVALID_STORY_FILE_RV;\r
+ for (i=24;i<81;i+=4)\r
+ if (read_alan_int(sf+i) > extent/4) return INVALID_STORY_FILE_RV;\r
+ for (i=160;i<(bf*4);i++)\r
+ crc+=sf[i];\r
+ if (crc!=read_alan_int(sf+152)) return INVALID_STORY_FILE_RV;\r
+ return VALID_STORY_FILE_RV;\r
+ }\r
+ else\r
+ { /* Identify Alan 3 */\r
+   bf=read_alan_int(sf+12);\r
+   if (bf > (extent/4)) return INVALID_STORY_FILE_RV;\r
+   for (i=184;i<(bf*4);i++)\r
+    crc+=sf[i];\r
+ if (crc!=read_alan_int(sf+176)) return INVALID_STORY_FILE_RV;\r
+\r
+ }\r
+ return INVALID_STORY_FILE_RV;\r
+}\r
diff --git a/babel/babel b/babel/babel
new file mode 100644 (file)
index 0000000..1808ec1
Binary files /dev/null and b/babel/babel differ
diff --git a/babel/babel-makefile b/babel/babel-makefile
new file mode 100644 (file)
index 0000000..1b31887
--- /dev/null
@@ -0,0 +1,75 @@
+# provisional makefile for babel\r
+#\r
+# Note that to compile babel, it is necessary only to compile all the .c\r
+# files in this distribution and link them.\r
+#\r
+# This makefile is provided purely as a convenience.\r
+#\r
+# The following targets are available:\r
+#  babel:              make babel\r
+#  babel.lib:          make babel handler library (for Borland)\r
+#  ifiction.lib:       make babel ifiction library (for Borland)\r
+#  babel.a:            make babel handler library (for gcc)\r
+#  ifiction.a:         make babel ifiction library (for gcc)\r
+#  dist:               make babel.zip, the babel source distribution\r
+#\r
+# Note that this is a GNU makefile, and may not work with other makes\r
+#\r
+# Comment/uncomment the following lines to make the program work\r
+\r
+#CC=bcc32\r
+#OBJ=.obj\r
+#BABEL_LIB=babel.lib\r
+#IFICTION_LIB=ifiction.lib\r
+#BABEL_FLIB=babel_functions.lib\r
+#OUTPUT_BABEL=\r
+\r
+CC=gcc -g\r
+OBJ=.o\r
+BABEL_LIB=babel.a\r
+BABEL_FLIB=babel_functions.a\r
+IFICTION_LIB=ifiction.a\r
+OUTPUT_BABEL=-o babel\r
+\r
+treaty_objs = zcode${OBJ} magscrolls${OBJ} blorb${OBJ} glulx${OBJ} hugo${OBJ} agt${OBJ} level9${OBJ} executable${OBJ} advsys${OBJ} tads${OBJ} tads2${OBJ} tads3${OBJ} adrift${OBJ} alan${OBJ}\r
+bh_objs = babel_handler${OBJ} register${OBJ} misc${OBJ} md5${OBJ} ${treaty_objs}\r
+ifiction_objs = ifiction${OBJ} register_ifiction${OBJ}\r
+babel_functions =  babel_story_functions${OBJ} babel_ifiction_functions${OBJ} babel_multi_functions${OBJ}\r
+babel_objs = babel${OBJ} $(BABEL_FLIB) $(IFICTION_LIB) $(BABEL_LIB)\r
+\r
+babel: ${babel_objs} \r
+       ${CC} ${OUTPUT_BABEL} ${babel_objs}\r
+\r
+%${OBJ} : %.c\r
+       ${CC} -c $^\r
+\r
+register${OBJ}: modules.h\r
+\r
+babel.lib: ${foreach dep,${bh_objs},${dep}.bl}\r
+\r
+ifiction.lib: ${foreach dep,${ifiction_objs},${dep}.il}\r
+\r
+babel_functions.lib: ${foreach dep,${babel_functions},${dep}.fl}\r
+\r
+%.obj.bl: %.obj\r
+       tlib babel.lib +-$^\r
+       echo made > $@\r
+\r
+%.obj.il: %.obj\r
+       tlib ifiction.lib +-$^\r
+       echo made > $@\r
+%.obj.fl: %.obj\r
+       tlib babel_functions.lib +-$^\r
+       echo made > $@\r
+\r
+babel.a: $(bh_objs)\r
+       ar -r babel.a $^\r
+\r
+ifiction.a: $(ifiction_objs)\r
+       ar -r ifiction.a $^\r
+\r
+babel_functions.a: $(babel_functions)\r
+       ar -r babel_functions.a $^\r
+\r
+dist: \r
+       cut -c0-31 MANIFEST | zip babel.zip -@\r
diff --git a/babel/babel.a b/babel/babel.a
new file mode 100644 (file)
index 0000000..142fb78
Binary files /dev/null and b/babel/babel.a differ
diff --git a/babel/babel.c b/babel/babel.c
new file mode 100644 (file)
index 0000000..5bb7213
--- /dev/null
@@ -0,0 +1,248 @@
+/* babel.c   The babel command line program\r
+ * (c) 2006 By L. Ross Raszewski\r
+ *\r
+ * This code is freely usable for all purposes.\r
+ *\r
+ * This work is licensed under the Creative Commons Attribution2.5 License.\r
+ * To view a copy of this license, visit\r
+ * http://creativecommons.org/licenses/by/2.5/ or send a letter to\r
+ * Creative Commons,\r
+ * 543 Howard Street, 5th Floor,\r
+ * San Francisco, California, 94105, USA.\r
+ *\r
+ * This file depends upon misc.c and babel.h\r
+ *\r
+ * This file exports one variable: char *rv, which points to the file name\r
+ * for an ifiction file.  This is used only by babel_ifiction_verify\r
+ */\r
+\r
+#include "babel.h"\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif\r
+int chdir(const char *);\r
+char *getcwd(char *, int);\r
+#ifdef __cplusplus\r
+}\r
+#endif\r
+\r
+char *fn;\r
+\r
+/* checked malloc function */\r
+void *my_malloc(int, char *);\r
+\r
+/* babel performs several fundamental operations, which are specified\r
+   by command-line objects. Each of these functions corresponds to\r
+   a story function (defined in babel_story_functions.c) or an\r
+   ifiction function (defined in babel_ifiction_functions.c) or both.\r
+   These are the types of those functions.\r
+*/\r
+\r
+typedef void (*story_function)(void);\r
+typedef void (*ifiction_function)(char *);\r
+typedef void (*multi_function)(char **, char *, int);\r
+/* This structure tells babel what to do with its command line arguments.\r
+   if either of story or ifiction are NULL, babel considers this command line\r
+   option inappropriate for that type of file.\r
+*/\r
+struct function_handler {\r
+        char *function;         /* the textual command line option */\r
+        story_function story;   /* handler for story files */\r
+        ifiction_function ifiction; /* handler for ifiction files */\r
+        char *desc;             /* Textual description for help text */\r
+        };\r
+\r
+struct multi_handler {\r
+        char *function;\r
+        char *argnames;\r
+        multi_function handler;\r
+        int nargsm;\r
+        int nargsx;\r
+        char *desc;\r
+        };\r
+/* This is an array of function_handler objects which specify the legal\r
+   arguments.  It is terminated by a function_handler with a NULL function\r
+ */\r
+static struct function_handler functions[] = {\r
+        { "-ifid", babel_story_ifid, babel_ifiction_ifid, "Deduce IFID"},\r
+        { "-format", babel_story_format, NULL, "Deduce story format" },\r
+        { "-ifiction", babel_story_ifiction, NULL, "Extract iFiction file" },\r
+        { "-meta", babel_story_meta, NULL, "Print story metadata" },\r
+        { "-identify", babel_story_identify, NULL, "Describe story file" },\r
+        { "-cover", babel_story_cover, NULL, "Extract cover art" },\r
+        { "-story", babel_story_story, NULL, "Extract story file (ie. from a blorb)" },\r
+        { "-verify", NULL, babel_ifiction_verify, "Verify integrity of iFiction file" },\r
+        { "-lint", NULL, babel_ifiction_lint, "Verify style of iFiction file" },\r
+        { "-fish", babel_story_fish, babel_ifiction_fish, "Extract all iFiction and cover art"},\r
+        { "-unblorb", babel_story_unblorb, NULL, "As -fish, but also extract story files"},\r
+        { NULL, NULL, NULL }\r
+        };\r
+static struct multi_handler multifuncs[] = {\r
+        { "-blorb", "<storyfile> <ifictionfile> [<cover art>]", babel_multi_blorb, 2, 3, "Bundle story file and (sparse) iFiction into blorb" },\r
+        { "-blorbs", "<storyfile> <ifictionfile> [<cover art>]", babel_multi_blorb1, 2, 3, "Bundle story file and (sparse) iFiction into sensibly-named blorb" },\r
+        { "-complete", "<storyfile> <ifictionfile>", babel_multi_complete, 2, 2, "Create complete iFiction file from sparse iFiction" },\r
+        { NULL, NULL, NULL, 0, 0, NULL }\r
+};\r
+\r
+int main(int argc, char **argv)\r
+{\r
+ char *todir=".";\r
+ char cwd[512];\r
+ int ok=1,i, l, ll;\r
+ FILE *f;\r
+ char *md=NULL;\r
+ /* Set the input filename.  Note that if this is invalid, babel should\r
+   abort before anyone notices\r
+ */\r
+ fn=argv[2];\r
+\r
+ if (argc < 3) ok=0;\r
+ /* Detect the presence of the "-to <directory>" argument.\r
+  */\r
+ if (ok && argc >=5 && strcmp(argv[argc-2], "-to")==0)\r
+ {\r
+  todir=argv[argc-1];\r
+  argc-=2;\r
+ }\r
+ if (ok) for(i=0;multifuncs[i].function;i++)\r
+          if (strcmp(argv[1],multifuncs[i].function)==0 &&\r
+              argc>= multifuncs[i].nargsm+2 &&\r
+              argc <= multifuncs[i].nargsx+2)\r
+          {\r
+\r
+   multifuncs[i].handler(argv+2, todir, argc-2);\r
+   exit(0);\r
+          }\r
+\r
+ if (argc!=3) ok=0;\r
+\r
+ /* Find the apropriate function_handler */\r
+ if (ok) {\r
+ for(i=0;functions[i].function && strcmp(functions[i].function,argv[1]);i++);\r
+ if (!functions[i].function) ok=0;\r
+ else  if (strcmp(fn,"-")) {\r
+   f=fopen(argv[2],"r");\r
+   if (!f) ok=0;\r
+  }\r
+ }\r
+\r
+ /* Print usage error if anything has gone wrong */\r
+ if (!ok)\r
+ {\r
+  printf("%s: Treaty of Babel Analysis Tool (%s, %s)\n"\r
+         "Usage:\n", argv[0],BABEL_VERSION, TREATY_COMPLIANCE);\r
+  for(i=0;functions[i].function;i++)\r
+  {\r
+   if (functions[i].story)\r
+    printf(" babel %s <storyfile>\n",functions[i].function);\r
+   if (functions[i].ifiction)\r
+    printf(" babel %s <ifictionfile>\n",functions[i].function);\r
+   printf("   %s\n",functions[i].desc);\r
+  }\r
+  for(i=0;multifuncs[i].function;i++)\r
+  {\r
+   printf("babel %s %s\n   %s\n",\r
+           multifuncs[i].function,\r
+           multifuncs[i].argnames,\r
+           multifuncs[i].desc);\r
+  }\r
+\r
+  printf ("\nFor functions which extract files, add \"-to <directory>\" to the command\n"\r
+          "to set the output directory.\n"\r
+          "The input file can be specified as \"-\" to read from standard input\n"\r
+          "(This may only work for .iFiction files)\n");\r
+  return 1;\r
+ }\r
+\r
+ /* For story files, we end up reading the file in twice.  This\r
+    is unfortunate, but unavoidable, since we want to be all\r
+    cross-platformy, so the first time we read it in, we\r
+    do the read in text mode, and the second time, we do it in binary\r
+    mode, and there are platforms where this makes a difference.\r
+ */\r
+ ll=0;\r
+ if (strcmp(fn,"-"))\r
+ {\r
+  fseek(f,0,SEEK_END);\r
+  l=ftell(f)+1;\r
+  fseek(f,0,SEEK_SET);\r
+  md=(char *)my_malloc(l,"Input file buffer");\r
+  fread(md,1,l-1,f);\r
+  md[l-1]=0;\r
+ }\r
+ else\r
+  while(!feof(stdin))\r
+  {\r
+   char *tt, mdb[1024];\r
+   int ii;\r
+   ii=fread(mdb,1,1024,stdin);\r
+   tt=(char *)my_malloc(ll+ii,"file buffer");\r
+   if (md) { memcpy(tt,md,ll); free(md); }\r
+   memcpy(tt+ll,mdb,ii);\r
+   md=tt;\r
+   ll+=ii;\r
+   if (ii<1024) break;\r
+  }\r
+\r
+\r
+  if (strstr(md,"<?xml version=") && strstr(md,"<ifindex"))\r
+  { /* appears to be an ifiction file */\r
+   char *pp;\r
+   pp=strstr(md,"</ifindex>");\r
+   if (pp) *(pp+10)=0;\r
+   getcwd(cwd,512);\r
+   chdir(todir);\r
+   l=0;\r
+   if (functions[i].ifiction)\r
+    functions[i].ifiction(md);\r
+   else\r
+    fprintf(stderr,"Error: option %s is not valid for iFiction files\n",\r
+             argv[1]);\r
+   chdir(cwd);\r
+ }\r
+\r
+ if (strcmp(fn,"-"))\r
+ {\r
+ free(md);\r
+ fclose(f);\r
+ }\r
+ if (l)\r
+ { /* Appears to be a story */\r
+   char *lt;\r
+   if (functions[i].story)\r
+   {\r
+    if (strcmp(fn,"-")) lt=babel_init(argv[2]);\r
+    else { lt=babel_init_raw(md,ll);\r
+           free(md);\r
+         }\r
+\r
+    if (lt)\r
+    {\r
+     getcwd(cwd,512);\r
+     chdir(todir);\r
+     if (!babel_get_authoritative() && strcmp(argv[1],"-format"))\r
+      printf("Warning: Story format could not be positively identified. Guessing %s\n",lt);\r
+     functions[i].story();\r
+\r
+     chdir(cwd);\r
+    }\r
+    else if (strcmp(argv[1],"-ifid")==0) /* IFID is calculable for all files */\r
+    {\r
+     babel_md5_ifid(cwd,512);\r
+     printf("IFID: %s\n",cwd);\r
+    }\r
+    else\r
+     fprintf(stderr,"Error: Did not recognize format of story file\n");\r
+    babel_release();\r
+   }\r
+   else\r
+    fprintf(stderr,"Error: option %s is not valid for story files\n",\r
+             argv[1]);\r
+  }    \r
+\r
+ return 0;\r
+}\r
diff --git a/babel/babel.h b/babel/babel.h
new file mode 100644 (file)
index 0000000..4108e3d
--- /dev/null
@@ -0,0 +1,56 @@
+/* babel.h  declarations for babel\r
+ * (c) 2006 By L. Ross Raszewski\r
+ *\r
+ * This code is freely usable for all purposes.\r
+ *\r
+ * This work is licensed under the Creative Commons Attribution2.5 License.\r
+ * To view a copy of this license, visit\r
+ * http://creativecommons.org/licenses/by/2.5/ or send a letter to\r
+ * Creative Commons,\r
+ * 543 Howard Street, 5th Floor,\r
+ * San Francisco, California, 94105, USA.\r
+ *\r
+ * This file depends upon treaty.h, babel_ifiction_functions.c,\r
+ * babel_story_functions.c, and babel_handler.c\r
+ *\r
+ */\r
+\r
+#define BABEL_VERSION "0.2b"\r
+\r
+#include "treaty.h"\r
+#include "babel_handler.h"\r
+#include "ifiction.h"\r
+/* Functions from babel_story_functions.c\r
+ *\r
+ * Each of these assumes that the story file has been loaded by babel_handler\r
+ *\r
+ * Each function babel_story_XXXX coresponds to the command line option -XXXX\r
+ */\r
+void babel_story_ifid(void);\r
+void babel_story_cover(void);\r
+void babel_story_ifiction(void);\r
+void babel_story_meta(void);\r
+void babel_story_fish(void);\r
+void babel_story_format(void);\r
+void babel_story_identify(void);\r
+void babel_story_story(void);\r
+void babel_story_unblorb(void);\r
+/* Functions from babel_ifiction_functions.c\r
+ *\r
+ * as with babel_story_XXXX, but for metadata, which is handed in as the\r
+ * C string parameter\r
+ */\r
+void babel_ifiction_ifid(char *);\r
+void babel_ifiction_verify(char *);\r
+void babel_ifiction_fish(char *);\r
+void babel_ifiction_lint(char *);\r
+\r
+/* Functions from babel_multi_functions.c\r
+ *\r
+ */\r
+void babel_multi_blorb(char **, char * , int);\r
+void babel_multi_blorb1(char **, char * , int);\r
+void babel_multi_complete(char **, char *, int);\r
+\r
+/* uncomment this line on platforms which limit extensions to 3 characters */\r
+/* #define THREE_LETTER_EXTENSIONS */\r
diff --git a/babel/babel_functions.a b/babel/babel_functions.a
new file mode 100644 (file)
index 0000000..ea8a1f6
Binary files /dev/null and b/babel/babel_functions.a differ
diff --git a/babel/babel_handler.c b/babel/babel_handler.c
new file mode 100644 (file)
index 0000000..f7afb78
--- /dev/null
@@ -0,0 +1,366 @@
+/* babel_handler.c   dispatches Treaty of Babel queries to the treaty modules\r
+ * (c) 2006 By L. Ross Raszewski\r
+ *\r
+ * This code is freely usable for all purposes.\r
+ *\r
+ * This work is licensed under the Creative Commons Attribution2.5 License.\r
+ * To view a copy of this license, visit\r
+ * http://creativecommons.org/licenses/by/2.5/ or send a letter to\r
+ * Creative Commons,\r
+ * 543 Howard Street, 5th Floor,\r
+ * San Francisco, California, 94105, USA.\r
+ *\r
+ * This file depends upon register.c, misc.c, babel.h, and treaty.h\r
+ * and L. Peter Deutsch's md5.c\r
+ * usage:\r
+ *  char *babel_init(char *filename)\r
+ *      Initializes babel to use the specified file. MUST be called before\r
+ *      babel_treaty.  Returns the human-readable name of the format\r
+ *      or NULL if the format is not known. Do not call babel_treaty unless\r
+ *      babel_init returned a nonzero value.\r
+ *      The returned string will be the name of a babel format, possibly\r
+ *      prefixed by "blorbed " to indicate that babel will process this file\r
+ *      as a blorb.\r
+ * int32 babel_treaty(int32 selector, void *output, void *output_extent)\r
+ *      Dispatches the call to the treaty handler for the currently loaded\r
+ *      file.\r
+ *      When processing a blorb, all treaty calls will be deflected to the\r
+ *      special blorb handler.  For the case of GET_STORY_FILE_IFID_SEL,\r
+ *      The treaty handler for the underlying format will be called if an\r
+ *      IFID is not found in the blorb resources.\r
+ * void babel_release()\r
+ *      Frees all resources allocated during babel_init.\r
+ *      You should do this even if babel_init returned NULL.\r
+ *      After this is called, do not call babel_treaty until after\r
+ *      another successful call to babel_init.\r
+ * char *babel_get_format()\r
+ *      Returns the same value as the last call to babel_init (ie, the format name)\r
+ * int32 babel_md5_ifid(char *buffer, int extent);\r
+ *      Generates an MD5 IFID from the loaded story.  Returns zero if something\r
+ *      went seriously wrong.\r
+ *\r
+ * If you wish to use babel in multiple threads, you must use the contextualized\r
+ * versions of the above functions.\r
+ * Each function above has a companion function whose name ends in _ctx (eg.\r
+ * "babel_treaty_ctx") which takes one additional argument.  This argument is\r
+ * the babel context. A new context is returned by void *ctx=get_babel_ctx(),\r
+ * and should be released when finished by calling release_babel_ctx(ctx);\r
+ */\r
+\r
+                      \r
+#include "treaty.h"\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <stdio.h>\r
+#include <ctype.h>\r
+#include "md5.h"\r
+\r
+void *my_malloc(int, char *);\r
+\r
+struct babel_handler\r
+{\r
+ TREATY treaty_handler;\r
+ TREATY treaty_backup;\r
+ void *story_file;\r
+ int32 story_file_extent;\r
+ void *story_file_blorbed;\r
+ int32 story_file_blorbed_extent;\r
+ char blorb_mode;\r
+ char *format_name;\r
+ char auth;\r
+};\r
+\r
+static struct babel_handler default_ctx;\r
+\r
+extern TREATY treaty_registry[];\r
+extern TREATY container_registry[];\r
+\r
+static char *deeper_babel_init(char *story_name, void *bhp)\r
+{\r
+ struct babel_handler *bh=(struct babel_handler *) bhp;\r
+ int i;\r
+ char *ext;\r
+\r
+ static char buffer[TREATY_MINIMUM_EXTENT];\r
+ int best_candidate;\r
+ char buffert[TREATY_MINIMUM_EXTENT];\r
+\r
+ if (story_name)\r
+  {\r
+   ext=strrchr(story_name,'.');\r
+   if (ext) for(i=0;ext[i];i++) ext[i]=tolower(ext[i]);\r
+  }\r
+ else ext=NULL;\r
+ best_candidate=-1;\r
+ if (ext) /* pass 1: try best candidates */\r
+  for(i=0;container_registry[i];i++)\r
+   if (container_registry[i](GET_FILE_EXTENSIONS_SEL,NULL,0,buffer,TREATY_MINIMUM_EXTENT) >=0 &&\r
+       strstr(buffer,ext) &&\r
+       container_registry[i](CLAIM_STORY_FILE_SEL,bh->story_file,bh->story_file_extent,NULL,0)>=NO_REPLY_RV)\r
+    break;\r
+  if (!ext || !container_registry[i]) /* pass 2: try all candidates */\r
+  {\r
+  \r
+  for(i=0;container_registry[i];i++)\r
+   {int l=container_registry[i](CLAIM_STORY_FILE_SEL,bh->story_file,bh->story_file_extent,NULL,0);\r
+    \r
+    if (l==VALID_STORY_FILE_RV)\r
+    break;\r
+    else if (l==NO_REPLY_RV && best_candidate < 0) best_candidate=i;\r
+    }\r
+}\r
+ if (!container_registry[i] && best_candidate >=0) { bh->auth=0; i=best_candidate; }\r
+ if (container_registry[i])\r
+ {\r
+   char buffer2[TREATY_MINIMUM_EXTENT];\r
+   \r
+   bh->treaty_handler=container_registry[i];\r
+   container_registry[i](GET_FORMAT_NAME_SEL,NULL,0,buffert,TREATY_MINIMUM_EXTENT);\r
+   bh->blorb_mode=1;\r
+\r
+   bh->story_file_blorbed_extent=container_registry[i](CONTAINER_GET_STORY_EXTENT_SEL,bh->story_file,bh->story_file_extent,NULL,0);\r
+   if (bh->story_file_blorbed_extent>0) bh->story_file_blorbed=my_malloc(bh->story_file_blorbed_extent, "contained story file");\r
+   if (bh->story_file_blorbed_extent<=0 ||\r
+       container_registry[i](CONTAINER_GET_STORY_FORMAT_SEL,bh->story_file,bh->story_file_extent,buffer2,TREATY_MINIMUM_EXTENT)<0 ||\r
+       container_registry[i](CONTAINER_GET_STORY_FILE_SEL,bh->story_file,bh->story_file_extent,bh->story_file_blorbed,bh->story_file_blorbed_extent)<=0\r
+      )\r
+    return NULL;\r
\r
+   for(i=0;treaty_registry[i];i++)\r
+    if (treaty_registry[i](GET_FORMAT_NAME_SEL,NULL,0,buffer,TREATY_MINIMUM_EXTENT)>=0 &&\r
+        strcmp(buffer,buffer2)==0 &&\r
+        treaty_registry[i](CLAIM_STORY_FILE_SEL,bh->story_file_blorbed,bh->story_file_blorbed_extent,NULL,0)>=NO_REPLY_RV)\r
+     break;\r
+  if (!treaty_registry[i])\r
+   return NULL;\r
+  bh->treaty_backup=treaty_registry[i];\r
+  sprintf(buffer,"%sed %s",buffert,buffer2);\r
+  return buffer;\r
+  }\r
+\r
+ bh->blorb_mode=0;\r
+ best_candidate=-1;\r
+\r
+ if (ext) /* pass 1: try best candidates */\r
+  for(i=0;treaty_registry[i];i++)\r
+   if (treaty_registry[i](GET_FILE_EXTENSIONS_SEL,NULL,0,buffer,TREATY_MINIMUM_EXTENT) >=0 &&\r
+       strstr(buffer,ext) && \r
+       treaty_registry[i](CLAIM_STORY_FILE_SEL,bh->story_file,bh->story_file_extent,NULL,0)>=NO_REPLY_RV)\r
+    break;\r
+  if (!ext || !treaty_registry[i]) /* pass 2: try all candidates */\r
+  {\r
+  \r
+  for(i=0;treaty_registry[i];i++)\r
+   {int l;\r
+   l=treaty_registry[i](CLAIM_STORY_FILE_SEL,bh->story_file,bh->story_file_extent,NULL,0);\r
+\r
+    if (l==VALID_STORY_FILE_RV)\r
+    break;\r
+    else if (l==NO_REPLY_RV && best_candidate < 0) best_candidate=i;\r
+    }\r
+  }\r
+  if (!treaty_registry[i])\r
+   if (best_candidate>0) { i=best_candidate; bh->auth=0; }\r
+   else return NULL;\r
+  bh->treaty_handler=treaty_registry[i];\r
+\r
+  if (bh->treaty_handler(GET_FORMAT_NAME_SEL,NULL,0,buffer,TREATY_MINIMUM_EXTENT)>=0)\r
+  return buffer;\r
+  return NULL;\r
+\r
+\r
+}\r
+\r
+static char *deep_babel_init(char *story_name, void *bhp)\r
+{\r
+ struct babel_handler *bh=(struct babel_handler *) bhp;\r
+ FILE *file;\r
+\r
+ bh->treaty_handler=NULL;\r
+ bh->treaty_backup=NULL;\r
+ bh->story_file=NULL;\r
+ bh->story_file_extent=0;\r
+ bh->story_file_blorbed=NULL;\r
+ bh->story_file_blorbed_extent=0;\r
+ bh->format_name=NULL;\r
+ file=fopen(story_name, "rb");\r
+ if (!file) return NULL;\r
+ fseek(file,0,SEEK_END);\r
+ bh->story_file_extent=ftell(file);\r
+ fseek(file,0,SEEK_SET);\r
+ bh->auth=1; \r
+ bh->story_file=my_malloc(bh->story_file_extent,"story file storage");\r
+ fread(bh->story_file,1,bh->story_file_extent,file);\r
+ fclose(file);\r
+\r
+ return deeper_babel_init(story_name, bhp);\r
+}\r
+\r
+char *babel_init_ctx(char *sf, void *bhp)\r
+{\r
+ struct babel_handler *bh=(struct babel_handler *) bhp;\r
+ char *b;\r
+ b=deep_babel_init(sf,bh);\r
+ if (b) bh->format_name=strdup(b);\r
+ return b;\r
+}\r
+char *babel_init(char *sf)\r
+{\r
+  return babel_init_ctx(sf, &default_ctx);\r
+}\r
+\r
+char *babel_init_raw_ctx(void *sf, int32 extent, void *bhp)\r
+{\r
+ struct babel_handler *bh=(struct babel_handler *) bhp;\r
+ char *b;\r
+ bh->treaty_handler=NULL;\r
+ bh->treaty_backup=NULL;\r
+ bh->story_file=NULL;\r
+ bh->story_file_extent=0;\r
+ bh->story_file_blorbed=NULL;\r
+ bh->story_file_blorbed_extent=0;\r
+ bh->format_name=NULL;\r
+ bh->story_file_extent=extent;\r
+ bh->auth=1; \r
+ bh->story_file=my_malloc(bh->story_file_extent,"story file storage");\r
+ memcpy(bh->story_file,sf,extent);\r
+\r
+ b=deeper_babel_init(NULL, bhp);\r
+ if (b) bh->format_name=strdup(b);\r
+ return b;\r
+}\r
+char *babel_init_raw(void *sf, int32 extent)\r
+{\r
+  return babel_init_raw_ctx(sf, extent, &default_ctx);\r
+}\r
+\r
+void babel_release_ctx(void *bhp)\r
+{\r
+ struct babel_handler *bh=(struct babel_handler *) bhp;\r
+ if (bh->story_file) free(bh->story_file);\r
+ bh->story_file=NULL;\r
+ if (bh->story_file_blorbed) free(bh->story_file_blorbed);\r
+ bh->story_file_blorbed=NULL;\r
+ if (bh->format_name) free(bh->format_name);\r
+ bh->format_name=NULL;\r
+}\r
+void babel_release()\r
+{\r
+ babel_release_ctx(&default_ctx);\r
+}\r
+int32 babel_md5_ifid_ctx(char *buffer, int32 extent, void *bhp)\r
+{\r
+ struct babel_handler *bh=(struct babel_handler *) bhp;\r
+ md5_state_t md5;\r
+ int i;\r
+ unsigned char ob[16];\r
+ if (extent <33 || bh->story_file==NULL)\r
+  return 0;\r
+ md5_init(&md5);\r
+ md5_append(&md5,bh->story_file,bh->story_file_extent);\r
+ md5_finish(&md5,ob);\r
+ for(i=0;i<16;i++)\r
+  sprintf(buffer+(2*i),"%02X",ob[i]);\r
+ buffer[32]=0;\r
+ return 1;\r
+\r
+}\r
+int32 babel_md5_ifid(char *buffer, int32 extent)\r
+{\r
+ return babel_md5_ifid_ctx(buffer, extent,\r
+                &default_ctx);\r
+}\r
+\r
+int32 babel_treaty_ctx(int32 sel, void *output, int32 output_extent,void *bhp)\r
+{\r
+ int32 rv;\r
+ struct babel_handler *bh=(struct babel_handler *) bhp;\r
+ if (!(sel & TREATY_SELECTOR_INPUT) && bh->blorb_mode)\r
+  rv=bh->treaty_backup(sel,bh->story_file_blorbed,bh->story_file_blorbed_extent,output, output_extent);\r
+ else\r
+ {\r
+  rv=bh->treaty_handler(sel,bh->story_file,bh->story_file_extent,output,output_extent);\r
+  if ((!rv|| rv==UNAVAILABLE_RV) && bh->blorb_mode)\r
+   rv=bh->treaty_backup(sel,bh->story_file_blorbed,bh->story_file_blorbed_extent,output, output_extent);\r
+  }\r
+ if (!rv && sel==GET_STORY_FILE_IFID_SEL)\r
+  return babel_md5_ifid_ctx(output,output_extent, bh);\r
+ if (rv==INCOMPLETE_REPLY_RV && sel==GET_STORY_FILE_IFID_SEL)\r
+  return babel_md5_ifid_ctx((void *)((char *) output+strlen((char *)output)),\r
+                            output_extent-strlen((char *)output),\r
+                            bh);\r
+\r
+ return rv;\r
+}\r
+int32 babel_treaty(int32 sel, void *output, int32 output_extent)\r
+{\r
+ return babel_treaty_ctx(sel, output, output_extent, &default_ctx);\r
+}\r
+char *babel_get_format_ctx(void *bhp)\r
+{\r
+ struct babel_handler *bh=(struct babel_handler *) bhp;\r
+ return bh->format_name;\r
+}\r
+char *babel_get_format()\r
+{\r
+ return babel_get_format_ctx(&default_ctx);\r
+}\r
+void *get_babel_ctx()\r
+{\r
+ return my_malloc(sizeof(struct babel_handler), "babel handler context");\r
+}\r
+void release_babel_ctx(void *b)\r
+{\r
+ free(b);\r
+}\r
+\r
+int32 babel_get_length_ctx(void *bhp)\r
+{\r
+ struct babel_handler *bh=(struct babel_handler *) bhp;\r
+ return bh->story_file_extent;\r
+}\r
+int32 babel_get_length()\r
+{\r
+ return babel_get_length_ctx(&default_ctx);\r
+}\r
+\r
+int32 babel_get_authoritative_ctx(void *bhp)\r
+{\r
+ struct babel_handler *bh=(struct babel_handler *) bhp;\r
+ return bh->auth;\r
+}\r
+int32 babel_get_authoritative()\r
+{\r
+  return babel_get_authoritative_ctx(&default_ctx);\r
+}\r
+void *babel_get_file_ctx(void *bhp)\r
+{\r
+ struct babel_handler *bh=(struct babel_handler *) bhp;\r
+ return bh->story_file;\r
+}\r
+void *babel_get_file()\r
+{\r
+ return babel_get_file_ctx(&default_ctx);\r
+}\r
+\r
+int32 babel_get_story_length_ctx(void *ctx)\r
+{\r
+  struct babel_handler *bh=(struct babel_handler *) ctx;\r
+  if (bh->blorb_mode) return bh->story_file_blorbed_extent;\r
+  return bh->story_file_extent;\r
+}\r
+int32 babel_get_story_length()\r
+{\r
+\r
+ return babel_get_story_length_ctx(&default_ctx);\r
+}\r
+void *babel_get_story_file_ctx(void *ctx)\r
+{\r
+  struct babel_handler *bh=(struct babel_handler *) ctx;\r
+  if (bh->blorb_mode) return bh->story_file_blorbed;\r
+  return bh->story_file;\r
+}\r
+void *babel_get_story_file()\r
+{\r
+ return babel_get_story_file_ctx(&default_ctx);\r
+}\r
diff --git a/babel/babel_handler.h b/babel/babel_handler.h
new file mode 100644 (file)
index 0000000..a01194a
--- /dev/null
@@ -0,0 +1,65 @@
+/* babel_handler.h  declarations for the babel handler API\r
+ * (c) 2006 By L. Ross Raszewski\r
+ *\r
+ * This code is freely usable for all purposes.\r
+ *\r
+ * This work is licensed under the Creative Commons Attribution2.5 License.\r
+ * To view a copy of this license, visit\r
+ * http://creativecommons.org/licenses/by/2.5/ or send a letter to\r
+ * Creative Commons,\r
+ * 543 Howard Street, 5th Floor,\r
+ * San Francisco, California, 94105, USA.\r
+ *\r
+ */\r
+\r
+#ifndef BABEL_HANDLER_H\r
+#define BABEL_HANDLER_H\r
+\r
+#include "treaty.h"\r
+\r
+/* Functions from babel_handler.c */\r
+char *babel_init(char *filename);\r
+ /* initialize the babel handler */\r
+char *babel_init_raw(void *sf, int32 extent);\r
+ /* Initialize from loaded data */\r
+int32 babel_treaty(int32 selector, void *output, int32 output_extent);\r
+ /* Dispatch treaty calls */\r
+void babel_release(void);\r
+ /* Release babel_handler resources */\r
+char *babel_get_format(void);\r
+ /* return the format of the loaded file */\r
+int32 babel_md5_ifid(char *buffer, int32 extent);\r
+ /* IFID generator of last resort */\r
+int32 babel_get_length(void);\r
+ /* Fetch file length */\r
+int32 babel_get_story_length(void);\r
+ /* Fetch file length */\r
+int32 babel_get_authoritative(void);\r
+ /* Determine if babel handler has a good grasp on the format */\r
+void *babel_get_file(void);\r
+ /* Get loaded story file */\r
+void *babel_get_story_file(void);\r
+ /* Get loaded story file */\r
+\r
+/* threadsafe versions of above */\r
+char *babel_init_ctx(char *filename, void *);\r
+ /* initialize the babel handler */\r
+int32 babel_treaty_ctx(int32 selector, void *output, int32 output_extent, void *);\r
+ /* Dispatch treaty calls */\r
+void babel_release_ctx(void *);\r
+ /* Release babel_handler resources */\r
+char *babel_get_format_ctx(void *);\r
+ /* return the format of the loaded file */\r
+int32 babel_md5_ifid_ctx(char *buffer, int extent, void *);\r
+ /* IFID generator of last resort */\r
+int32 babel_get_length_ctx(void *);\r
+int32 babel_get_story_length_ctx(void *);\r
+void *babel_get_file_ctx(void *bhp);\r
+void *babel_get_story_ctx(void *bhp);\r
+int32 babel_get_authoritative_ctx(void *bhp);\r
+char *babel_init_raw_ctx(void *sf, int32 extent, void *bhp);\r
+void *get_babel_ctx(void);\r
+void release_babel_ctx(void *);\r
+ /* get and release babel contexts */\r
+\r
+#endif\r
diff --git a/babel/babel_ifiction_functions.c b/babel/babel_ifiction_functions.c
new file mode 100644 (file)
index 0000000..e59adf6
--- /dev/null
@@ -0,0 +1,168 @@
+/* babel_ifiction_functions.c babel top-level operations for ifiction\r
+ * (c) 2006 By L. Ross Raszewski\r
+ *\r
+ * This code is freely usable for all purposes.\r
+ *\r
+ * This work is licensed under the Creative Commons Attribution2.5 License.\r
+ * To view a copy of this license, visit\r
+ * http://creativecommons.org/licenses/by/2.5/ or send a letter to\r
+ * Creative Commons,\r
+ * 543 Howard Street, 5th Floor,\r
+ * San Francisco, California, 94105, USA.\r
+ *\r
+ * This file depends upon babel.c (for rv), babel.h, misc.c and ifiction.c\r
+ */\r
+\r
+#include "babel.h"\r
+#include <string.h>\r
+#include <stdlib.h>\r
+#include <stdio.h>\r
+#include <ctype.h>\r
+\r
+#ifndef THREE_LETTER_EXTENSIONS\r
+#define IFICTION_EXT ".iFiction"\r
+#else\r
+#define IFICTION_EXT ".ifi"\r
+#endif\r
+\r
+void *my_malloc(int, char *);\r
+\r
+struct IFiction_Info\r
+{\r
+ char ifid[256];\r
+ int wmode;\r
+};\r
+\r
+static void write_story_to_disk(struct XMLTag *xtg, void *ctx)\r
+{\r
+ char *b, *ep;\r
+ char *begin, *end;\r
+ char buffer[TREATY_MINIMUM_EXTENT];\r
+ int32 l, j;\r
+ if (ctx) { }\r
+\r
+ if (strcmp(xtg->tag,"story")==0)\r
+ {\r
+ begin=xtg->begin;\r
+ end=xtg->end;\r
+ l=end-begin+1;\r
+ b=(char *)my_malloc(l,"XML buffer");\r
+ memcpy(b,begin,l-1);\r
+ b[l]=0;\r
+ j=ifiction_get_IFID(b,buffer,TREATY_MINIMUM_EXTENT);\r
+ if (!j)\r
+ {\r
+  fprintf(stderr,"No IFID found for this story\n");\r
+  free(b);\r
+  return;\r
+ }\r
+ ep=strtok(buffer,",");\r
+ while(ep)\r
+ {\r
+  char buf2[256];\r
+  FILE *f;\r
+  sprintf(buf2,"%s%s",ep,IFICTION_EXT);\r
+  f=fopen(buf2,"w");\r
+\r
+  if (!f ||\r
+  fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"\r
+        "<!-- Metadata extracted by Babel -->"\r
+        "<ifindex version=\"1.0\" xmlns=\"http://babel.ifarchive.org/protocol/iFiction/\">\n"\r
+        " <story>",\r
+        f)==EOF ||\r
+  fputs(b,f)==EOF ||\r
+  fputs("/<story>\n</ifindex>\n",f)==EOF\r
+  )\r
+  {\r
+   fprintf(stderr,"Error writing to file %s\n",buf2);\r
+  } else\r
+   printf("Extracted %s\n",buf2);\r
+  if (f) fclose(f);\r
+\r
+  ep=strtok(NULL,",");\r
+ }\r
+\r
+ free(b);\r
+ }\r
+}\r
+\r
+void babel_ifiction_ifid(char *md)\r
+{\r
+ char output[TREATY_MINIMUM_EXTENT];\r
+ int i;\r
+ char *ep;\r
+ i=ifiction_get_IFID(md,output,TREATY_MINIMUM_EXTENT);\r
+ if (!i)\r
+\r
+ {\r
+  fprintf(stderr,"Error: No IFIDs found in iFiction file\n");\r
+  return;\r
+ }\r
+ ep=strtok(output,",");\r
+ while(ep)\r
+ {\r
+  printf("IFID: %s\n",ep);\r
+  ep=strtok(NULL,",");\r
+ }\r
+\r
+}\r
+\r
+static char isok;\r
+\r
+static void examine_tag(struct XMLTag *xtg, void *ctx)\r
+{\r
+ struct IFiction_Info *xti=(struct IFiction_Info *)ctx;\r
+\r
+ if (strcmp("ifid",xtg->tag)==0 && strcmp(xti->ifid,"UNKNOWN")==0)\r
+ {\r
+ memcpy(xti->ifid,xtg->begin,xtg->end-xtg->begin);\r
+ xti->ifid[xtg->end-xtg->begin]=0;\r
+ }\r
+\r
+}\r
+static void verify_eh(char *e, void *ctx)\r
+{\r
+ if (*((int *)ctx) < 0) return;\r
+ if (*((int *)ctx) || strncmp(e,"Warning",7))\r
+  { isok=0;\r
+   fprintf(stderr, "%s\n",e);\r
+  }\r
+}\r
+\r
+\r
+\r
+void babel_ifiction_fish(char *md)\r
+{\r
+ int i=-1;\r
+ ifiction_parse(md,write_story_to_disk,NULL,verify_eh,&i);\r
+}\r
+\r
+void deep_ifiction_verify(char *md, int f)\r
+{\r
+ struct IFiction_Info ii;\r
+ int i=0;\r
+ ii.wmode=0;\r
+ isok=1;\r
+ strcpy(ii.ifid,"UNKNOWN");\r
+ ifiction_parse(md,examine_tag,&ii,verify_eh,&i);\r
+ if (f&& isok) printf("Verified %s\n",ii.ifid);\r
+}\r
+void babel_ifiction_verify(char *md)\r
+{\r
+ deep_ifiction_verify(md,1);\r
+\r
+}\r
+\r
+\r
+void babel_ifiction_lint(char *md)\r
+{\r
+ struct IFiction_Info ii;\r
+ int i=1;\r
+ ii.wmode=1;\r
+ isok=1;\r
+ strcpy(ii.ifid,"UNKNOWN");\r
+ ifiction_parse(md,examine_tag,&ii,verify_eh,&i);\r
+ if (isok) printf("%s conforms to iFiction style guidelines\n",ii.ifid);\r
+}\r
+\r
+\r
diff --git a/babel/babel_multi_functions.c b/babel/babel_multi_functions.c
new file mode 100644 (file)
index 0000000..cd867c6
--- /dev/null
@@ -0,0 +1,312 @@
+#include "babel.h"\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <ctype.h>\r
+\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif\r
+int chdir(const char *);\r
+char *getcwd(char *, int);\r
+#ifdef __cplusplus\r
+}\r
+#endif\r
+\r
+void deep_ifiction_verify(char *md, int f);\r
+void * my_malloc(int32, char *);\r
+char *blorb_chunk_for_name(char *name);\r
+#ifndef THREE_LETTER_EXTENSIONS\r
+static char *ext_table[] = { "zcode", ".zblorb",\r
+                             "glulx", ".gblorb",\r
+                             NULL, NULL\r
+                             };\r
+#else\r
+static char *ext_table[] = { "zcode", ".zlb",\r
+                             "glulx", ".glb",\r
+                             NULL, NULL\r
+                             };\r
+\r
+#endif\r
+char *blorb_ext_for_name(char *fmt)\r
+{\r
+ int i;\r
+ for(i=0;ext_table[i];i+=2)\r
+  if (strcmp(ext_table[i],fmt)==0) return ext_table[i+1];\r
+#ifndef THREE_LETTER_EXTENSIONS\r
+ return ".blorb";\r
+#else\r
+ return ".blb";\r
+#endif\r
+}\r
+\r
+char *deep_complete_ifiction(char *fn, char *ifid, char *format)\r
+{\r
+ FILE *f;\r
+ int32 i;\r
+ char *md;\r
+ char *id, *idp;\r
+ char *idb;\r
+ f=fopen(fn,"r");\r
+ if (!f) { fprintf(stderr,"Error: Can not open file %s\n",fn);\r
+          return NULL;\r
+          }\r
+ fseek(f,0,SEEK_END);\r
+ i=ftell(f);\r
+ fseek(f,0,SEEK_SET);\r
+ md=(char *) my_malloc(i+1,"Metadata buffer");\r
+ fread(md,1,i,f);\r
+ md[i]=0;\r
+ id=strstr(md,"</ifindex>");\r
+ if (id) *(id+10)=0;\r
+ fclose(f);\r
+ id=strdup(ifid);\r
+ idp=strtok(id,",");\r
+ /* Find the identification chunk */\r
+ {\r
+   char *bp, *ep;\r
+   bp=strstr(md,"<identification>");\r
+   if (!bp)\r
+   {\r
+    idb=(char *)my_malloc(TREATY_MINIMUM_EXTENT+128,"ident buffer");\r
+    sprintf(idb,"<format>%s</format>\n", format);\r
+   }\r
+   else\r
+   {\r
+    int ii;\r
+    ep=strstr(bp,"</identification>");\r
+    idb=(char *)my_malloc(TREATY_MINIMUM_EXTENT+128+(ep-bp),"ident buffer");\r
+    for(ii=16;bp+ii<ep;ii++)\r
+    idb[ii-16]=bp[ii];\r
+    idb[ii]=0;\r
+    for(ep+=18;*ep;ep++)\r
+     *bp++=*ep;\r
+    *bp=0;\r
+    bp=strstr(idb,"<format>");\r
+    if (bp)\r
+     if (memcmp(bp+8,format,strlen(format)))\r
+      fprintf(stderr,"Error: Format in sparse .iFiction does not match story\n");\r
+\r
+   }\r
+\r
+ }\r
+ /* Insert the new ifids */\r
+ while(idp)\r
+ {\r
+  char bfr[TREATY_MINIMUM_EXTENT];\r
+  sprintf(bfr,"<ifid>%s</ifid>",idp);\r
+  if (!strstr(idb,bfr)) { strcat(idb,bfr); strcat(idb,"\n"); }\r
+  idp=strtok(NULL,",");\r
+\r
+ }\r
+ free(id);\r
+ idp=(char *) my_malloc(strlen(md)+strlen(idb)+64, "Output metadata");\r
+/* printf("%d bytes for metadata\n",strlen(md)+strlen(idb)+64);*/\r
+ id=strstr(md,"<story>");\r
+ id[0]=0;\r
+ id+=7;\r
+ strcpy(idp,md);\r
+ strcat(idp,"<story>\n <identification>\n");\r
+ strcat(idp,idb);\r
+ free(idb);\r
+ strcat(idp," </identification>\n");\r
+ strcat(idp,id);\r
+ free(md);\r
+ md=idp;\r
+ deep_ifiction_verify(md, 0);\r
+ return md;\r
+}\r
+\r
+void write_int(int32 i, FILE *f)\r
+{\r
+ char bf[4];\r
+ bf[0]=(((unsigned) i) >> 24) & 0xFF;\r
+ bf[1]=(((unsigned) i) >> 16) & 0xFF;\r
+ bf[2]=(((unsigned) i) >> 8) & 0xFF;\r
+ bf[3]=(((unsigned) i)) & 0xFF;\r
+ fwrite(bf,1,4,f);\r
+}\r
+static void _babel_multi_blorb(char *outfile, char **args, char *todir , int argc)\r
+{\r
+ int32 total, storyl, coverl, i;\r
+ char buffer[TREATY_MINIMUM_EXTENT+10];\r
+ char b2[TREATY_MINIMUM_EXTENT];\r
+\r
+ char cwd[512];\r
+ char *cover, *md, *cvrf, *ep;\r
+\r
+ FILE *f, *c;\r
+ if (argc!=2 && argc !=3)\r
+ {\r
+  fprintf(stderr,"Invalid usage\n");\r
+  return;\r
+ }\r
+ if (!babel_init(args[0]))\r
+ {\r
+  fprintf(stderr,"Error: Could not determine the format of file %s\n",args[0]);\r
+  return;\r
+ }\r
+ if (babel_treaty(GET_STORY_FILE_IFID_SEL,buffer,TREATY_MINIMUM_EXTENT)<=0 ||\r
+     babel_treaty(GET_FORMAT_NAME_SEL,b2,TREATY_MINIMUM_EXTENT)<0\r
+    )\r
+ {\r
+  fprintf(stderr,"Error: Could not deduce an IFID for file %s\n",args[0]);\r
+  return;\r
+ }\r
+ if (babel_get_length() != babel_get_story_length())\r
+ {\r
+  fprintf(stderr,"Warning: Story file will be extacted from container before blorbing\n");\r
+ }\r
+/* printf("Completing ifiction\n");*/\r
+ md=deep_complete_ifiction(args[1],buffer,b2);\r
+/* printf("Ifiction is %d bytes long\n",strlen(md));*/\r
+ ep=strchr(buffer,',');\r
+ if (ep) *ep=0;\r
+ if (outfile)\r
+  strcpy(buffer,outfile);\r
+ strcat(buffer,blorb_ext_for_name(b2));\r
+ getcwd(cwd,512);\r
+ chdir(todir);\r
+ f=fopen(buffer,"wb");\r
+ chdir(cwd);\r
+ if (!f)\r
+ {\r
+  fprintf(stderr,"Error: Error writing to file %s\n",buffer);\r
+  return;\r
+ }\r
+ storyl=babel_get_story_length();\r
+ total=storyl + (storyl%2) + 36;\r
+ if (md) total+=8+strlen(md)+strlen(md)%2;\r
+ if (argc==3)\r
+ {\r
+  c=fopen(args[2],"rb");\r
+  if (c)\r
+  {\r
+   fseek(c,0,SEEK_END);\r
+   coverl=ftell(c);\r
+   if (coverl > 5){\r
+\r
+   cover=(char *) my_malloc(coverl+2,"Cover art buffer");\r
+   fseek(c,0,SEEK_SET);\r
+   fread(cover,1,coverl,c);\r
+   if (memcmp(cover+1,"PNG",3)==0) cvrf="PNG ";\r
+   else cvrf="JPEG";\r
+   total += 32+coverl + (coverl%2);\r
+   }\r
+   else argc=2;\r
+   fclose(c);\r
+  }\r
+  else argc=2;\r
+ }\r
+/* printf("Writing header\n;");*/\r
+ fwrite("FORM",1,4,f);\r
+ write_int(total,f);\r
+/* printf("Writing index\n;");*/\r
+ fwrite("IFRSRIdx",1,8,f);\r
+ write_int(argc==3 ? 28:16,f);\r
+ write_int(argc==3 ? 2:1,f);\r
+/* printf("Writing story\n;");*/\r
+ fwrite("Exec", 1,4,f);\r
+ write_int(0,f);\r
+ write_int(argc==3 ? 48:36,f);\r
+ if (argc==3)\r
+ {\r
+/* printf("Writing image\n;"); */\r
+ fwrite("Pict", 1,4,f);\r
+ write_int(1,f);\r
+ write_int(56+storyl+(storyl%2),f);\r
+ }\r
+/* printf("Invoking chunk for name %s\n",b2); */\r
+ fwrite(blorb_chunk_for_name(b2),1,4,f);\r
+ write_int(storyl,f);\r
+/* printf("Writing story data\n"); */\r
+ fwrite(babel_get_story_file(),1,storyl,f);\r
+ if (storyl%2) fwrite("\0",1,1,f);\r
+ if (argc==3)\r
+ {\r
+/*  printf("Writing cover data header %s\n",cvrf); */\r
+  fwrite(cvrf,1,4,f);\r
+/*  printf("Writing cover data size %d\n",coverl); */\r
+  write_int(coverl,f);\r
+/*  printf("Writing cover data\n"); */\r
+  fwrite(cover,1,coverl,f);\r
+  if (coverl%2) fwrite("\0",1,1,f);\r
+/*  printf("Done with cover\n");*/\r
+/*  free(cover);*/\r
+/*  printf("Writing frontispiece\n;");*/\r
+  fwrite("Fspc\0\0\0\004\0\0\0\001",1,12,f);\r
+ }\r
+\r
+ if (md) {\r
+/*  printf("Writing metadata\n;");*/\r
+ fwrite("IFmd",1,4,f);\r
+ write_int(strlen(md),f);\r
+ fwrite(md,1,strlen(md),f);\r
+ if (strlen(md)%2)\r
+  fwrite("\0",1,1,f);\r
+ free(md);\r
+ }\r
+\r
+ fclose(f);\r
+ printf("Created %s\n",buffer);\r
\r
+}\r
+void babel_multi_complete(char **args, char *todir, int argc)\r
+{\r
+ char buffer[TREATY_MINIMUM_EXTENT+10];\r
+  char b2[TREATY_MINIMUM_EXTENT];\r
+ char cwd[512];\r
+ char *ep, *md;\r
+ FILE *f;\r
+ if (argc!=2)\r
+ {\r
+  fprintf(stderr,"Invalid usage\n");\r
+  return;\r
+ }\r
+ if (!babel_init(args[0]))\r
+ {\r
+  fprintf(stderr,"Error: Could not determine the format of file %s\n",args[0]);\r
+  return;\r
+ }\r
+ if (babel_treaty(GET_STORY_FILE_IFID_SEL,buffer,TREATY_MINIMUM_EXTENT)<=0\r
+    ||  babel_treaty(GET_FORMAT_NAME_SEL,b2,TREATY_MINIMUM_EXTENT)<0)\r
+ {\r
+  fprintf(stderr,"Error: Could not deduce an IFID for file %s\n",args[0]);\r
+  return;\r
+ }\r
+ md=deep_complete_ifiction(args[1],buffer, b2);\r
+ if (!md) return;\r
+ ep=strchr(buffer,',');\r
+ if (ep) *ep=0;\r
+ strcat(buffer,".iFiction");\r
+ getcwd(cwd,512);\r
+ chdir(todir);\r
+ f=fopen(buffer,"w");\r
+ chdir(cwd);\r
+ if (!f || !fputs(md,f))\r
+ {\r
+  fprintf(stderr,"Error: Error writing to file %s\n",buffer);\r
+  return;\r
+ }\r
+ fclose(f);\r
+ free(md);\r
+ printf("Created %s\n",buffer);\r
+}\r
+void babel_multi_blorb(char **args, char *todir , int argc)\r
+{\r
+ _babel_multi_blorb(NULL,args,todir,argc);\r
+}\r
+void babel_multi_blorb1(char **args, char *todir , int argc)\r
+{\r
+ char *buf;\r
+ char *bb;\r
+ buf=(char *)my_malloc(strlen(args[0])+1,"blorb name buffer");\r
+ strcpy(buf,args[0]);\r
+ bb=strrchr(buf,'.');\r
+ if (bb) *bb=0;\r
+ _babel_multi_blorb(buf,args,todir,argc);\r
+ free(buf);\r
\r
+\r
+}\r
diff --git a/babel/babel_story_functions.c b/babel/babel_story_functions.c
new file mode 100644 (file)
index 0000000..b1cf0e9
--- /dev/null
@@ -0,0 +1,411 @@
+/* babel_story_functions.c babel top-level operations for story files\r
+ * (c) 2006 By L. Ross Raszewski\r
+ *\r
+ * This code is freely usable for all purposes.\r
+ *\r
+ * This work is licensed under the Creative Commons Attribution2.5 License.\r
+ * To view a copy of this license, visit\r
+ * http://creativecommons.org/licenses/by/2.5/ or send a letter to\r
+ * Creative Commons,\r
+ * 543 Howard Street, 5th Floor,\r
+ * San Francisco, California, 94105, USA.\r
+ *\r
+ * This file depends upon babel_handler.c, babel.h, and misc.c\r
+ */\r
+\r
+#include "babel.h"\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+\r
+#ifndef THREE_LETTER_EXTENSIONS\r
+#define IFICTION_EXT ".iFiction"\r
+#else\r
+#define IFICTION_EXT ".ifi"\r
+#endif\r
+void *my_malloc(int32, char *);\r
+\r
+void babel_story_ifid()\r
+{\r
+  char buffer[TREATY_MINIMUM_EXTENT];\r
+  char *ep;\r
+  int i;\r
+  i=babel_treaty(GET_STORY_FILE_IFID_SEL,buffer,TREATY_MINIMUM_EXTENT);\r
+  ep=strtok(buffer, ",");\r
+  while(ep)\r
+  {\r
+   printf("IFID: %s\n",ep);\r
+   ep=strtok(NULL,",");\r
+  }\r
+  if (!i)\r
+    fprintf(stderr,"Unable to create an IFID (A serious problem occurred while loading the file).\n");\r
+\r
+}\r
+\r
+\r
+void babel_story_format()\r
+{\r
+  char *b;\r
+  b=babel_get_format();\r
+  if (!b) b="unknown";\r
+  if (!babel_get_authoritative())\r
+   printf("Format: %s (non-authoritative)\n",b);   \r
+  else printf("Format: %s\n",b);\r
+}\r
+\r
+static void deep_babel_ifiction(char stopped)\r
+{\r
+  char buffer[TREATY_MINIMUM_EXTENT];\r
+  char *md;\r
+  char *ep;\r
+  int32 i;\r
+  FILE *f;\r
+\r
+  if (stopped!=2)\r
+  {\r
+  i=babel_treaty(GET_STORY_FILE_IFID_SEL,buffer,TREATY_MINIMUM_EXTENT);\r
+  if (i==0 && !babel_md5_ifid(buffer, TREATY_MINIMUM_EXTENT))\r
+  {\r
+   fprintf(stderr,"Unable to create an IFID (A serious problem occurred while loading the file).\n");\r
+   return;\r
+  }\r
+\r
+\r
+  ep=strtok(buffer, ",");\r
+  }\r
+  else ep="-";\r
+  i=babel_treaty(GET_STORY_FILE_METADATA_EXTENT_SEL,NULL,0);\r
+  if (i<=0)\r
+  {\r
+   if (stopped) printf("No iFiction record for %s\n",buffer);\r
+   return;\r
+  }\r
+  md=(char *)my_malloc(i,"Metadata buffer");\r
+  if (babel_treaty(GET_STORY_FILE_METADATA_SEL,md,i)<0)\r
+  {\r
+    fprintf(stderr,"A serious error occurred while retrieving metadata.\n");\r
+    free(md);\r
+    return;\r
+  }\r
+  while(ep)\r
+  {\r
+   char epb[TREATY_MINIMUM_EXTENT+9];\r
+   if (stopped!=2)\r
+   {\r
+   strcpy(epb,ep);\r
+   strcat(epb, IFICTION_EXT);\r
+\r
+   f=fopen(epb,"w");\r
+   }\r
+   else f=stdout;\r
+\r
+   if (!f || fputs(md,f)==EOF)\r
+    fprintf(stderr,"A serious error occurred writing to disk.\n");\r
+   else if (stopped!=2) printf("Extracted %s\n",epb);\r
+   if (f) fclose(f);\r
+   if (stopped) break;\r
+   ep=strtok(NULL,",");\r
+  }\r
+  free(md);\r
+}\r
+\r
+void babel_story_ifiction()\r
+{\r
+ deep_babel_ifiction(1);\r
+}\r
+static char *get_jpeg_dim(void *img, int32 extent)\r
+{\r
+  unsigned char *dp=(unsigned char *) img;\r
+  unsigned char *ep=dp+extent;\r
+  static char buffer[256];\r
+  unsigned int t1, t2, w, h;\r
+\r
+\r
+  t1=*(dp++);\r
+  t2=*(dp++);\r
+  if (t1!=0xff || t2!=0xD8 )\r
+  {\r
+   return "(invalid)";\r
+  }\r
+\r
+  while(1)\r
+  {\r
+   if (dp>ep) return "(invalid)";\r
+   for(t1=*(dp++);t1!=0xff;t1=*(dp++)) if (dp>ep) return "(invalid)";\r
+   do { t1=*(dp++); if (dp>ep) return "(invalid 4)";} while (t1 == 0xff);\r
+\r
+   if ((t1 & 0xF0) == 0xC0 && !(t1==0xC4 || t1==0xC8 || t1==0xCC))\r
+   {\r
+    dp+=3;\r
+    if (dp>ep) return "(invalid)";\r
+    h=*(dp++) << 8;\r
+    if (dp>ep) return "(invalid)";\r
+    h|=*(dp++);\r
+    if (dp>ep) return "(invalid)";\r
+    w=*(dp++) << 8;\r
+    if (dp>ep) return "(invalid)";\r
+    w|=*(dp);\r
+    sprintf(buffer, "(%dx%d)",w,h);\r
+    return buffer;\r
+   }\r
+   else if (t1==0xD8 || t1==0xD9)\r
+    break;\r
+   else\r
+   { int l;\r
+\r
+    if (dp>ep) return "(invalid)";\r
+     l=*(dp++) << 8;\r
+    if (dp>ep) return "(invalid)";\r
+     l|= *(dp++);\r
+     l-=2;\r
+     dp+=l;\r
+     if (dp>ep) return "(invalid)";\r
+    }\r
+   }\r
+  return "(invalid)";\r
+}\r
+\r
+static int32 read_int(unsigned char  *mem)\r
+{\r
+  int32 i4 = mem[0],\r
+                    i3 = mem[1],\r
+                    i2 = mem[2],\r
+                    i1 = mem[3];\r
+  return i1 | (i2<<8) | (i3<<16) | (i4<<24);\r
+}\r
+\r
+\r
+static char *get_png_dim(void *img, int32 extent)\r
+{\r
+ unsigned char *dp=(unsigned char *)img;\r
+ static char buffer[256];\r
+ int32 w, h;\r
+ if (extent<33 ||\r
+ !(dp[0]==137 && dp[1]==80 && dp[2]==78 && dp[3]==71 &&\r
+        dp[4]==13 && dp[5] == 10 && dp[6] == 26 && dp[7]==10)||\r
+ !(dp[12]=='I' && dp[13]=='H' && dp[14]=='D' && dp[15]=='R'))\r
+ return "(invalid)";\r
+ w=read_int(dp+16);\r
+ h=read_int(dp+20);\r
+ sprintf(buffer,"(%dx%d)",w,h);\r
+ return buffer;\r
+}\r
+static char *get_image_dim(void *img, int32 extent, int fmt)\r
+{\r
+ if (fmt==JPEG_COVER_FORMAT) return get_jpeg_dim(img,extent);\r
+ else if (fmt==PNG_COVER_FORMAT) return get_png_dim(img, extent);\r
+ return "(unknown)";\r
+\r
+}\r
+static void deep_babel_cover(char stopped)\r
+{\r
+  char buffer[TREATY_MINIMUM_EXTENT];\r
+  void *md;\r
+  char *ep;\r
+  char *ext;\r
+  char *dim;\r
+  int32 i,j;\r
+  FILE *f;\r
+  i=babel_treaty(GET_STORY_FILE_IFID_SEL,buffer,TREATY_MINIMUM_EXTENT);\r
+  if (i==0)\r
+   if (babel_md5_ifid(buffer, TREATY_MINIMUM_EXTENT))\r
+    printf("IFID: %s\n",buffer);\r
+   else\r
+    {\r
+     fprintf(stderr,"Unable to create an IFID (A serious problem occurred while loading the file).\n");\r
+     return;\r
+    }\r
+  else \r
+\r
+  ep=strtok(buffer, ",");\r
+  i=babel_treaty(GET_STORY_FILE_COVER_EXTENT_SEL,NULL,0);\r
+  j=babel_treaty(GET_STORY_FILE_COVER_FORMAT_SEL,NULL,0);\r
+\r
+  if (i<=0 || j<=0)\r
+  {\r
+   if (stopped) printf("No cover art for %s\n",buffer);\r
+   return;\r
+  }\r
+  if (j==PNG_COVER_FORMAT) ext=".png";\r
+  else if (j==JPEG_COVER_FORMAT) ext=".jpg";\r
+  md=my_malloc(i,"Image buffer");\r
+  if (babel_treaty(GET_STORY_FILE_COVER_SEL,md,i)<0)\r
+  {\r
+    fprintf(stderr,"A serious error occurred while retrieving cover art.\n");\r
+    free(md);\r
+    return;\r
+  }\r
+  dim=get_image_dim(md,i,j);\r
+  while(ep)\r
+  {\r
+   char epb[TREATY_MINIMUM_EXTENT+9];\r
+   strcpy(epb,ep);\r
+   strcat(epb, ext);\r
+\r
+   f=fopen(epb,"wb");\r
+   if (!f || fwrite(md,1,i,f)==EOF)\r
+    fprintf(stderr,"A serious error occurred writing to disk.\n");\r
+   else printf("Extracted %s %s\n",epb, dim);\r
+   if (f) fclose(f);\r
+   if (stopped) break;\r
+   ep=strtok(NULL,",");\r
+  }\r
+  free(md);\r
+}\r
+\r
+void babel_story_cover()\r
+{\r
+ deep_babel_cover(1);\r
+}\r
+\r
+void babel_story_fish()\r
+{\r
+ deep_babel_ifiction(0);\r
+ deep_babel_cover(0);\r
+}\r
+\r
+static char *get_biblio(void)\r
+{\r
+ int32 i;\r
+ char *md;\r
+ char *bib="No bibliographic data";\r
+ char *bibb; char *bibe; \r
+ char *t;\r
+ static char buffer[TREATY_MINIMUM_EXTENT];\r
+\r
+ i=babel_treaty(GET_STORY_FILE_METADATA_EXTENT_SEL,NULL,0);\r
+ if (i<=0) return bib;\r
+\r
+ md=(char *) my_malloc(i,"Metadata buffer");\r
+ if (babel_treaty(GET_STORY_FILE_METADATA_SEL,md,i)<0) return bib;\r
\r
+ bibb=strstr(md,"<bibliographic>");\r
+ if (!bibb) { free(md); return bib; }\r
+ bibe=strstr(bibb,"</bibliographic>");\r
+ if (bibe) *bibe=0;\r
+ t=strstr(bibb,"<title>");\r
+ if (t)\r
+ {\r
+  t+=7;\r
+  bibe=strstr(t,"</title>");\r
+  if (bibe)\r
+  {\r
+    *bibe=0;\r
+    bib=buffer;\r
+    for(i=0;t[i];i++) if (t[i]<0x20 || t[i]>0x7e) t[i]='_';\r
+    sprintf(buffer, "\"%s\" ",t);\r
+    *bibe='<';\r
+  }\r
+  else strcpy(buffer,"<no title found> ");\r
+ }\r
+ t=strstr(bibb,"<author>");\r
+ if (t)\r
+ {\r
+  t+=8;\r
+  bibe=strstr(t,"</author>");\r
+  if (bibe)\r
+  {\r
+    bib=buffer;\r
+    *bibe=0;\r
+    for(i=0;t[i];i++) if (t[i]<0x20 || t[i]>0x7e) t[i]='_';\r
+    strcat(buffer, "by ");\r
+    strcat(buffer, t);\r
+    *bibe='<';\r
+  }\r
+  else strcat(buffer, "<no author found>");\r
+ }\r
+ free(md);\r
+ return bib;\r
+\r
+}\r
+void babel_story_identify()\r
+{\r
+ int32 i, j, l;\r
+ char *b, *cf, *dim;\r
+ char buffer[TREATY_MINIMUM_EXTENT];\r
+\r
+ printf("%s\n",get_biblio());\r
+ babel_story_ifid();\r
+ b=babel_get_format();\r
+ if (!b) b="unknown";\r
+ l=babel_get_length() / 1024;\r
\r
+\r
+ i=babel_treaty(GET_STORY_FILE_COVER_EXTENT_SEL,NULL,0);\r
+ j=babel_treaty(GET_STORY_FILE_COVER_FORMAT_SEL,NULL,0);\r
+\r
+ if (i<=0 || j<=0)\r
+ {\r
+  cf="no cover"; \r
+ }\r
+ else\r
+ {\r
+  char *md=my_malloc(i,"Image buffer");\r
+  if (babel_treaty(GET_STORY_FILE_COVER_SEL,md,i)<0)\r
+  {\r
+   cf="no cover";\r
+  }\r
+  else\r
+  {\r
+   dim=get_image_dim(md,i,j)+1;\r
+   dim[strlen(dim)-1]=0;\r
+   if (j==JPEG_COVER_FORMAT) cf="jpeg";\r
+   else if (j==PNG_COVER_FORMAT) cf="png";\r
+   else cf="unknown format";\r
+   sprintf(buffer,"cover %s %s",dim,cf);\r
+   cf=buffer;\r
+  }\r
+ }\r
+ printf("%s, %dk, %s\n",b, l,cf);\r
+}\r
+\r
+void babel_story_meta()\r
+{\r
+ deep_babel_ifiction(2);\r
+}\r
+\r
+void babel_story_story()\r
+{\r
+  int32 j,i;\r
+  void *p;\r
+  FILE *f;\r
+  char *ep;\r
+  char buffer[TREATY_MINIMUM_EXTENT+20];\r
+  j=babel_get_story_length();\r
+  p=babel_get_story_file();\r
+  if (!j || !p)\r
+  {\r
+    fprintf(stderr,"A serious error occurred while retrieving the story file.\n");\r
+    return;\r
+  }\r
+\r
+  i=babel_treaty(GET_STORY_FILE_IFID_SEL,buffer,TREATY_MINIMUM_EXTENT);\r
+  if (i==0 && !babel_md5_ifid(buffer, TREATY_MINIMUM_EXTENT))\r
+  {\r
+   fprintf(stderr,"Unable to create an IFID (A serious problem occurred while loading the file).\n");\r
+   return;\r
+  }\r
+  ep=strchr(buffer, ',');\r
+  if (!ep) ep=buffer+strlen(buffer);\r
+  *ep=0;\r
+  babel_treaty(GET_STORY_FILE_EXTENSION_SEL,ep,19);\r
+  f=fopen(buffer,"wb");\r
+  if (!f || !fwrite(p,1,j,f))\r
+    {\r
+     fprintf(stderr,"A serious error occurred writing to disk.\n");\r
+     return;\r
+    }\r
+ fclose(f);\r
+ printf("Extracted %s\n",buffer);\r
+\r
+  \r
+\r
+}\r
+\r
+void babel_story_unblorb()\r
+{\r
+ deep_babel_ifiction(1);\r
+ deep_babel_cover(1);\r
+ babel_story_story();\r
+\r
+}\r
diff --git a/babel/blorb.c b/babel/blorb.c
new file mode 100644 (file)
index 0000000..ebb84e4
--- /dev/null
@@ -0,0 +1,245 @@
+/* blorb.c      Babel interface to blorb files\r
+ * Copyright 2006 by L. Ross Raszewski\r
+ *\r
+ * This code is freely usable for all purposes.\r
+ *\r
+ * This work is licensed under the Creative Commons Attribution2.5 License.\r
+ * To view a copy of this license, visit\r
+ * http://creativecommons.org/licenses/by/2.5/ or send a letter to\r
+ * Creative Commons,\r
+ * 543 Howard Street, 5th Floor,\r
+ * San Francisco, California, 94105, USA.\r
+ *\r
+ * This file depends upon treaty_builder.h, misc.c and ifiction.c\r
+ *\r
+ * Header note: to add support for new executable chunk types, see\r
+ * TranslateExec.\r
+ *\r
+ * This file defines a Treaty of Babel compatable module for handling blorb\r
+ * files.  However, blorb files are not themselves a babel format. This module\r
+ * is used internally by the babel program to handle blorbs.\r
+ *\r
+ * As a result, if GET_STORY_FILE_IFID_SEL returns NO_REPLY_RV,\r
+ * you should check the story file against the babel registry before resorting\r
+ * to the default IFID calculation.\r
+ *\r
+ */\r
+#define FORMAT blorb\r
+#define HOME_PAGE "http://eblong.com/zarf/blorb"\r
+#define FORMAT_EXT ".blorb,.blb,.zblorb,.zlb,.gblorb,.glb"\r
+#define CONTAINER_FORMAT\r
+#include "treaty_builder.h"\r
+#include <stdlib.h>\r
+#include <ctype.h>\r
+\r
+extern TREATY treaty_registry[];\r
+/* The following is the translation table of Blorb chunk types to\r
+   babel formats.  it is NULL-terminated. */\r
+static char *TranslateExec[] = { "ZCOD", "zcode",\r
+                                 "GLUL", "glulx",\r
+                                 "TAD2", "tads2",\r
+                                 "TAD3", "tads3",\r
+                                 NULL, NULL };\r
+\r
+void *my_malloc(int32, char *);\r
+int32 ifiction_get_IFID(char *, char *, int32);\r
+\r
+static int32 read_int(void *inp)\r
+{\r
+  unsigned char *mem=(unsigned char *)inp;\r
+  int32 i4 = mem[0],\r
+                    i3 = mem[1],\r
+                    i2 = mem[2],\r
+                    i1 = mem[3];\r
+  return i1 | (i2<<8) | (i3<<16) | (i4<<24);\r
+}\r
+\r
+\r
+static int32 blorb_get_chunk(void *blorb_file, int32 extent, char *id, int32 *begin, int32 *output_extent)\r
+{\r
+ int32 i=12, j;\r
+ while(i<extent-8)\r
+ {\r
+  if (memcmp(((char *)blorb_file)+i,id,4)==0)\r
+  {\r
+   *output_extent=read_int((char *)blorb_file+i+4);\r
+   if (*output_extent > extent) return NO_REPLY_RV;\r
+   *begin=i+8;\r
+   return 1;\r
+  }\r
+\r
+  j=read_int((char *)blorb_file+i+4);\r
+  if (j%2) j++;\r
+  i+=j+8;\r
+\r
+ }\r
+ return NO_REPLY_RV;\r
+}\r
+static int32 blorb_get_resource(void *blorb_file, int32 extent, char *rid, int32 number, int32 *begin, int32 *output_extent)\r
+{\r
+ int32 ridx_len;\r
+ int32 i,j;\r
+ void *ridx;\r
+ if (blorb_get_chunk(blorb_file, extent,"RIdx",&i,&ridx_len)==NO_REPLY_RV)\r
+  return NO_REPLY_RV;\r
+\r
+ ridx=(char *)blorb_file+i+4;\r
+ ridx_len=read_int((char *)blorb_file+i);\r
+ for(i=0;i<ridx_len;i++)\r
+ { \r
+  if(memcmp((char *)ridx+(i*12),rid,4)==0 && read_int((char *)ridx+(i*12)+4)==number)\r
+  {\r
+   j=i;\r
+   i=read_int((char *)ridx+(j*12)+8);\r
+   *begin=i+8;\r
+   *output_extent=read_int((char *)blorb_file+i+4);\r
+   return 1;\r
+  }\r
+ }\r
+ return NO_REPLY_RV;\r
+}\r
+static int32 blorb_get_story_file(void *blorb_file, int32 extent, int32 *begin, int32 *output_extent)\r
+{\r
+ return blorb_get_resource(blorb_file, extent, "Exec", 0, begin, output_extent);\r
+\r
+}\r
+\r
+static int32 get_story_extent(void *blorb_file, int32 extent)\r
+{\r
+ int32 i,j;\r
+ if (blorb_get_resource(blorb_file, extent, "Exec", 0, &i, &j))\r
+ {\r
+  return j;\r
+ }\r
+ return NO_REPLY_RV;\r
+\r
+}\r
+static int32 get_story_file(void *blorb_file, int32 extent, void *output, int32 output_extent)\r
+{\r
+ int32 i,j;\r
+ if (blorb_get_resource(blorb_file, extent, "Exec", 0, &i, &j))\r
+ {\r
+  ASSERT_OUTPUT_SIZE(j);\r
+  memcpy(output,(char *)blorb_file+i,j);\r
+  return j;\r
+ }\r
+ return NO_REPLY_RV;\r
+\r
+}\r
+\r
+char *blorb_chunk_for_name(char *name)\r
+{\r
+ static char buffer[5];\r
+ int j;\r
+ for(j=0;TranslateExec[j];j+=2)\r
+  if (strcmp(name,TranslateExec[j+1])==0) return TranslateExec[j];\r
+ for(j=0;j<4 && name[j];j++) buffer[j]=toupper(buffer[j]);\r
+ while(j<4) buffer[j++]=' ';\r
+ buffer[4]=0;\r
+ return buffer;\r
+\r
+}\r
+static char *blorb_get_story_format(void *blorb_file, int32 extent)\r
+{\r
+ int32 i, j;\r
+\r
+ for(j=0;treaty_registry[j];j++)\r
+ {\r
+  static char fn[512];\r
+  treaty_registry[j](GET_FORMAT_NAME_SEL,NULL,0,fn,512);\r
+  if (blorb_get_chunk(blorb_file,extent,blorb_chunk_for_name(fn),&i, &i)) return fn;\r
+ }\r
+ return NULL;\r
+}\r
+\r
+static int32 get_story_format(void *blorb_file, int32 extent, char *output, int32 output_extent)\r
+{\r
+ char *o;\r
+ o=blorb_get_story_format(blorb_file, extent);\r
+ if (!o) return NO_REPLY_RV;\r
+ ASSERT_OUTPUT_SIZE((signed) strlen(o)+1);\r
+ strcpy(output,o);\r
+ return strlen(o)+1;\r
+}\r
+static int32 get_story_file_metadata_extent(void *blorb_file, int32 extent)\r
+{\r
+ int32 i,j;\r
+ if (blorb_get_chunk(blorb_file,extent,"IFmd",&i,&j)) return j+1;\r
+ return NO_REPLY_RV;\r
+}\r
+static int32 blorb_get_cover(void *blorb_file, int32 extent, int32 *begin, int32 *output_extent)\r
+{\r
+ int i,j;\r
+ if (blorb_get_chunk(blorb_file,extent,"Fspc",&i,&j))\r
+ {\r
+  if (j<4) return NO_REPLY_RV;\r
+  i=read_int((char *)blorb_file+i);\r
+  if (!blorb_get_resource(blorb_file,extent,"Pict",i,&i,&j)) return NO_REPLY_RV;\r
+  *begin=i;\r
+  *output_extent=j;\r
+  if (memcmp((char *)blorb_file+i-8,"PNG ",4)==0) return PNG_COVER_FORMAT;\r
+  else if (memcmp((char *)blorb_file+i-8,"JPEG",4)==0) return JPEG_COVER_FORMAT;\r
+ }\r
+ return NO_REPLY_RV;\r
+\r
+}\r
+\r
+static int32 get_story_file_cover_extent(void *blorb_file, int32 extent)\r
+{\r
+ int32 i,j;\r
+ if (blorb_get_cover(blorb_file,extent,&i,&j)) return j;\r
+ return NO_REPLY_RV;\r
+}\r
+\r
+static int32 get_story_file_cover_format(void *blorb_file, int32 extent)\r
+{\r
+ int32 i,j;\r
+ return blorb_get_cover(blorb_file, extent, &i,&j);\r
+}\r
+\r
+static int32 get_story_file_IFID(void *b, int32 e, char *output, int32 output_extent)\r
+{\r
+ int32 j;\r
+ char *md;\r
+ j=get_story_file_metadata_extent(b,e);\r
+ if (j<=0) return NO_REPLY_RV;\r
+ md=(char *)my_malloc(j, "Metadata buffer");\r
+ j=get_story_file_metadata(b,e,md,j);\r
+ if (j<=0) return NO_REPLY_RV;\r
+\r
+ j=ifiction_get_IFID(md,output,output_extent);\r
+ free(md);\r
+ return j;\r
+}\r
+\r
+static int32 get_story_file_metadata(void *blorb_file, int32 extent, char *output, int32 output_extent)\r
+{\r
+ int32 i,j;\r
+ if (!blorb_get_chunk(blorb_file, extent,"IFmd",&i,&j)) return NO_REPLY_RV;\r
+ ASSERT_OUTPUT_SIZE(j+1);\r
+ memcpy(output,(char *)blorb_file+i,j);\r
+ output[j]=0;\r
+ return j+1;\r
+}\r
+static int32 get_story_file_cover(void *blorb_file, int32 extent, void *output, int32 output_extent)\r
+{\r
+ int32 i,j;\r
+ if (!blorb_get_cover(blorb_file, extent,&i,&j)) return NO_REPLY_RV;\r
+ ASSERT_OUTPUT_SIZE(j);\r
+ memcpy(output,(char *)blorb_file+i,j);\r
+ return j;\r
+}\r
+\r
+static int32 claim_story_file(void *story_file, int32 extent)\r
+{\r
+ int i;\r
+\r
+ if (extent<16 ||\r
+     memcmp(story_file,"FORM",4) ||\r
+     memcmp((char *)story_file+8,"IFRS",4) \r
+    ) i= INVALID_STORY_FILE_RV;\r
+ else i= NO_REPLY_RV;\r
\r
+ return i;\r
+\r
+}\r
diff --git a/babel/executable.c b/babel/executable.c
new file mode 100644 (file)
index 0000000..ac0c403
--- /dev/null
@@ -0,0 +1,62 @@
+/* executable.c  Treaty of Babel module for Z-code files\r
+ * 2006 By L. Ross Raszewski\r
+ *\r
+ * This file depends on treaty_builder.h\r
+ *\r
+ * This file is public domain, but note that any changes to this file\r
+ * may render it noncompliant with the Treaty of Babel\r
+ */\r
+\r
+#define FORMAT executable\r
+#define HOME_PAGE "http://http://en.wikipedia.org/wiki/Executable"\r
+#define FORMAT_EXT ".exe"\r
+#define NO_METADATA\r
+#define NO_COVER\r
+\r
+#include "treaty_builder.h"\r
+#include <ctype.h>\r
+#include <stdio.h>\r
+\r
+static char elfmagic[] = { 0x7f, 0x45, 0x4c, 0x46, 0 };\r
+static char javamagic[] = { 0xCA, 0xFE, 0xBA, 0xBE, 0 };\r
+static char amigamagic[] = { 0, 0, 3, 0xe7, 0 };\r
+static char machomagic[] = { 0xFE, 0xED, 0xFA, 0xCE, 0};\r
+struct exetype\r
+{\r
+ char *magic;\r
+ char *name;\r
+ int len;\r
+};\r
+static struct exetype magic[]= { { "MZ", "MZ", 2 },\r
+                                 { elfmagic, "ELF", 4 },\r
+                                 { javamagic, "JAVA", 4 },\r
+                                 { amigamagic, "AMIGA", 4 },\r
+                                 { "#! ", "SCRIPT", 3 },\r
+                                 { machomagic, "MACHO",4 },\r
+                                 { "APPL", "MAC",4 },\r
+                                 { NULL, NULL, 0 } };\r
+\r
+static char *deduce_magic(void *sf, int32 extent)\r
+{\r
+ int i;\r
+ for(i=0;magic[i].magic;i++)\r
+ if (extent >= magic[i].len && memcmp(magic[i].magic,sf,magic[i].len)==0)\r
+  return magic[i].name;\r
+ return NULL;\r
+}\r
+                                 \r
+static int32 claim_story_file(void *sf, int32 extent)\r
+{\r
+ if (deduce_magic(sf,extent)) return VALID_STORY_FILE_RV;\r
+ return NO_REPLY_RV;\r
+}\r
+static int32 get_story_file_IFID(void *sf, int32 extent, char *output, int32 output_extent)\r
+{\r
+ char *o;\r
+ o=deduce_magic(sf,extent);\r
+ if (!o) return 0;\r
+ ASSERT_OUTPUT_SIZE((signed) strlen(o)+2);\r
+ strcpy(output,o);\r
+ strcat(output,"-");\r
+ return INCOMPLETE_REPLY_RV;\r
+}\r
diff --git a/babel/glulx.c b/babel/glulx.c
new file mode 100644 (file)
index 0000000..ea1b3c4
--- /dev/null
@@ -0,0 +1,81 @@
+/* glulx.c  Treaty of Babel module for Glulx files\r
+ * 2006 By L. Ross Raszewski\r
+ *\r
+ * This file depends on treaty_builder.h\r
+ *\r
+ * This file is public domain, but note that any changes to this file\r
+ * may render it noncompliant with the Treaty of Babel\r
+ */\r
+\r
+#define FORMAT glulx\r
+#define HOME_PAGE "http://eblong.com/zarf/glulx"\r
+#define FORMAT_EXT ".ulx"\r
+#define NO_METADATA\r
+#define NO_COVER\r
+\r
+#include "treaty_builder.h"\r
+#include <ctype.h>\r
+#include <stdio.h>\r
+\r
+static int32 read_int(unsigned char  *mem)\r
+{\r
+  int32 i4 = mem[0],\r
+                    i3 = mem[1],\r
+                    i2 = mem[2],\r
+                    i1 = mem[3];\r
+  return i1 | (i2<<8) | (i3<<16) | (i4<<24);\r
+}\r
+\r
+\r
+\r
+static int32 get_story_file_IFID(void *story_file, int32 extent, char *output, int32 output_extent)\r
+{\r
+ int32 i,j, k;\r
+ char ser[7];\r
+ char buffer[32];\r
+\r
+\r
+ if (extent<256) return INVALID_STORY_FILE_RV;\r
+ for(i=0;i<extent;i++) if (memcmp((char *)story_file+i,"UUID://",7)==0) break;\r
+ if (i<extent) /* Found explicit IFID */\r
+  {\r
+   for(j=i+7;j<extent && ((char *)story_file)[j]!='/';j++);\r
+   if (j<extent)\r
+   {\r
+    i+=7;\r
+    ASSERT_OUTPUT_SIZE(j-i);\r
+    memcpy(output,(char *)story_file+i,j-i);\r
+    output[j-i]=0;\r
+    return 1;\r
+   }\r
+  }\r
+\r
+ /* Did not find intact IFID.  Build one */\r
+\r
+ j=read_int((unsigned char *)story_file+32);\r
+ k=read_int((unsigned char *)story_file+12);\r
+ if (memcmp((char *)story_file+36,"Info",4)==0)\r
+ { /* Inform generated */\r
+  char *bb=(char *)story_file+52;\r
+  k= (int) bb[0]<<8 | (int) bb[1];\r
+  memcpy(ser,bb+2,6);\r
+  ser[6]=0;\r
+  for(i=0;i<6;i++) if (!isalnum(ser[i])) ser[i]='-';\r
+  sprintf(buffer,"GLULX-%u-%s-%04X",k,ser,j);\r
+ }\r
+ else\r
+  sprintf(buffer,"GLULX-%08X-%08X",k,j);\r
+\r
+ ASSERT_OUTPUT_SIZE((signed) strlen(buffer)+1);\r
+ strcpy((char *)output,buffer);\r
+ return 1;\r
+\r
+}\r
+\r
+static int32 claim_story_file(void *story_file, int32 extent)\r
+{\r
+ if (extent<256 ||\r
+     memcmp(story_file,"Glul",4)\r
+    ) return INVALID_STORY_FILE_RV;\r
+ return VALID_STORY_FILE_RV;\r
+}\r
diff --git a/babel/hugo.c b/babel/hugo.c
new file mode 100644 (file)
index 0000000..42ad4f3
--- /dev/null
@@ -0,0 +1,79 @@
+/* hugo.c  Treaty of Babel module for hugo files\r
+ * 2006 By L. Ross Raszewski\r
+ *\r
+ * This file depends on treaty_builder.h\r
+ *\r
+ * This file is public domain, but note that any changes to this file\r
+ * may render it noncompliant with the Treaty of Babel\r
+ */\r
+\r
+#define FORMAT hugo\r
+#define HOME_PAGE "http://www.generalcoffee.com"\r
+#define FORMAT_EXT ".hex"\r
+#define NO_METADATA\r
+#define NO_COVER\r
+\r
+#include "treaty_builder.h"\r
+#include <ctype.h>\r
+#include <stdio.h>\r
+\r
+static int32 get_story_file_IFID(void *s_file, int32 extent, char *output, int32 output_extent)\r
+{\r
+\r
+ int32 i,j;\r
+ char ser[9];\r
+ char buffer[32];\r
+ char *story_file = (char *) s_file;\r
+\r
+\r
+ if (extent<0x0B) return INVALID_STORY_FILE_RV;\r
+\r
+ for(i=0;i<extent;i++) if (memcmp((char *)story_file+i,"UUID://",7)==0) break;\r
+ if (i<extent) /* Found explicit IFID */\r
+  {\r
+   for(j=i+7;j<extent && ((char *)story_file)[j]!='/';j++);\r
+   if (j<extent)\r
+   {\r
+    i+=7;\r
+    ASSERT_OUTPUT_SIZE(j-i);\r
+    memcpy(output,(char *)story_file+i,j-i);\r
+    output[j-i]=0;\r
+    return 1;\r
+   }\r
+  }\r
\r
+ memcpy(ser, (char *) story_file+0x03, 8);\r
+ ser[8]=0;\r
+\r
+ for(j=0;j<8;j++)\r
+  if (!isalnum(ser[j])) ser[j]='-';\r
+\r
+\r
+ sprintf(buffer,"HUGO-%d-%02X-%02X-%s",story_file[0],story_file[1], story_file[2],ser);\r
+\r
+ ASSERT_OUTPUT_SIZE((signed) strlen(buffer)+1);\r
+ strcpy((char *)output,buffer);\r
+ return 1;\r
+}\r
+\r
+static int32 read_hugo_addx(unsigned char *from)\r
+{\r
+ return ((unsigned int) from[0])| ((unsigned int)from[1] << 8);\r
+}\r
+\r
+static int32 claim_story_file(void *story_file, int32 extent)\r
+{\r
+ unsigned char *sf=(unsigned char *)story_file;\r
+ int32 i;\r
+ int32 scale;\r
+\r
+ if (!story_file || extent < 0x28) return  INVALID_STORY_FILE_RV;\r
+\r
+ if (sf[0]<34) scale=4;\r
+ else scale=16;\r
+ for(i=3;i<0x0B;i++) if (sf[i]<0x20 || sf[i]>0x7e) return INVALID_STORY_FILE_RV;\r
+ for(i=0x0b;i<0x18;i+=2)\r
+ if (read_hugo_addx(sf+i) * scale > extent) return INVALID_STORY_FILE_RV;\r
+\r
+ return VALID_STORY_FILE_RV;\r
+}\r
diff --git a/babel/ifiction.a b/babel/ifiction.a
new file mode 100644 (file)
index 0000000..6dda84f
Binary files /dev/null and b/babel/ifiction.a differ
diff --git a/babel/ifiction.c b/babel/ifiction.c
new file mode 100644 (file)
index 0000000..cb620e7
--- /dev/null
@@ -0,0 +1,534 @@
+/* ifiction.c  common babel interface for processing ifiction metadata\r
+ * (c) 2006 By L. Ross Raszewski\r
+ *\r
+ * This code is freely usable for all purposes.\r
+ *\r
+ * This work is licensed under the Creative Commons Attribution2.5 License.\r
+ * To view a copy of this license, visit\r
+ * http://creativecommons.org/licenses/by/2.5/ or send a letter to\r
+ * Creative Commons,\r
+ * 543 Howard Street, 5th Floor,\r
+ * San Francisco, California, 94105, USA.\r
+ *\r
+ * This file depends on treaty.h\r
+ *\r
+ * This file contains common routines for handling ifiction metadata strings\r
+ *\r
+ * int32 ifiction_get_IFID(char *metadata, char *output, int32 output_extent)\r
+ * does what the babel treaty function GET_STORY_FILE_IFID_SEL would do for ifiction\r
+ *\r
+ * void ifiction_parse(char *md, IFCloseTag close_tag, void *close_ctx,\r
+ *                     IFErrorHandler error_handler, void *error_ctx)\r
+ * parses the given iFiction metadata.  close_tag(struct XMLtag xtg, close_ctx)\r
+ * is called for each tag as it is closed, error_handler(char *error, error_ctx)\r
+ * is called each time a structural or logical error is found in the iFiction\r
+ * This is a very simple XML parser, and probably not as good as any "real"\r
+ * XML parser.  Its only two benefits are that (1) it's really small, and (2)\r
+ * it strictly checks the ifiction record against the Treaty of Babel\r
+ * requirements\r
+ *\r
+ */\r
+\r
+#include "ifiction.h"\r
+#include <string.h>\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <ctype.h>\r
+\r
+void *my_malloc(int, char *);\r
+extern char *format_registry[];\r
+\r
+\r
+static int32 llp;\r
+static char *lnlst;\r
+\r
+static char utfeol[3] = { 0xe2, 0x80, 0xa8 };\r
+static int32 getln(char *endp)\r
+{\r
+ for(;lnlst<endp;lnlst++) if (*lnlst=='\n' || memcmp(lnlst,utfeol,3)==0) llp++;\r
+ return llp;\r
+}\r
+\r
+\r
+static int32 ifiction_get_first_IFID(char *metadata, char *output, int32 output_extent)\r
+{\r
+ char *ifid_begin, *ifid_end;\r
\r
+ ifid_begin=strstr(metadata,"<ifid>");\r
+ if (!ifid_begin) return NO_REPLY_RV;\r
+ ifid_begin+=6;\r
+\r
+ ifid_end=strstr(ifid_begin,"</ifid>");\r
+ if (!ifid_end) return NO_REPLY_RV;\r
+ if (output_extent<=(ifid_end-ifid_begin)) return INVALID_USAGE_RV;\r
+\r
+ memcpy(output,ifid_begin,ifid_end-ifid_begin);\r
+\r
+ output[ifid_end-ifid_begin]=0;\r
+\r
+ return ifid_end-metadata+7;\r
+}\r
+\r
+\r
+int32 ifiction_get_IFID(char *metadata, char *output, int32 output_extent)\r
+{\r
+ int32 j=0, k;\r
+\r
+ while(*metadata)\r
+ {\r
+ if ((k=ifiction_get_first_IFID(metadata,output,output_extent)) <= 0) break;\r
+ j++;\r
+ metadata+=k;\r
+ output_extent-=strlen(output)+1;\r
+ output+=strlen(output);\r
+ *output=',';\r
+ output++;\r
+ }\r
+ if (*(output-1)==',') *(output-1)=0;\r
+ return j;\r
+}\r
+\r
+\r
+static char *leaf_tags[] = { "ifid",\r
+                             "format",\r
+                             "bafn",\r
+                             "title",\r
+                             "author",\r
+                             "headline",\r
+                             "firstpublished",\r
+                             "genre",\r
+                             "group",\r
+                             "description",\r
+                             "leafname",\r
+                             "url",\r
+                             "authoremail",\r
+                             "height",\r
+                             "width",\r
+\r
+                             NULL\r
+                             };\r
+static char *one_per[] = { "identification",\r
+                           "bibliographic",\r
+                           "format",\r
+                           "title",\r
+                           "author",\r
+                           "headline",\r
+                           "firstpublished",\r
+                           "genre",\r
+                           "group",\r
+                           "description",\r
+                           "leafname",\r
+                           "height",\r
+                           "width",\r
+                           "forgiveness",\r
+                           "colophon",\r
+                           NULL\r
+                         };\r
+\r
+static char *required[] = {\r
+                "cover", "height",\r
+                "cover", "width",\r
+                "cover", "format",\r
+                "resources", "auxiliary",\r
+                "auxiliary", "leafname",\r
+                "auxiliary", "description",\r
+                "ifiction", "story",\r
+                "story", "identification",\r
+                "story", "bibliographic",\r
+                "identification", "ifid",\r
+                "identification", "format",\r
+                "bibliographic", "title",\r
+                "bibliographic", "author",\r
+                "colophon", "generator",\r
+                "colophon", "originated",\r
+                NULL, NULL\r
+                };\r
+static char *zarfian[] = {\r
+        "Merciful",\r
+        "Polite",\r
+        "Tough",\r
+        "Nasty",\r
+        "Cruel",\r
+        NULL\r
+        };\r
+\r
+struct ifiction_info {\r
+        int32 width;\r
+        int32 height;\r
+        int format;\r
+        };\r
+static void ifiction_validate_tag(struct XMLTag *xtg, struct ifiction_info *xti, IFErrorHandler err_h, void *ectx)\r
+{\r
+ int i;\r
+ char ebuf[512];\r
+ struct XMLTag *parent=xtg->next;\r
+ if (parent)\r
+ {\r
+ for(i=0;leaf_tags[i];i++)\r
+  if (strcmp(parent->tag,leaf_tags[i])==0)\r
+   {\r
+    sprintf(ebuf, "Error: (line %d) Tag <%s> is not permitted within tag <%s>",\r
+        xtg->beginl,xtg->tag,parent->tag);\r
+    err_h(ebuf,ectx);\r
+    }\r
+ for(i=0;required[i];i+=2)\r
+ if (strcmp(required[i],parent->tag)==0 && strcmp(required[i+1],xtg->tag)==0)\r
+  parent->rocurrences[i]=1;\r
+ for(i=0;one_per[i];i++)\r
+ if (strcmp(one_per[i],xtg->tag)==0)\r
+  if (parent->occurences[i]) { \r
+                               sprintf(ebuf,"Error: (line %d) Found more than one <%s> within <%s>",xtg->beginl,xtg->tag,\r
+                                        parent->tag);\r
+                               err_h(ebuf,ectx);\r
+                             }\r
+   else parent->occurences[i]=1;\r
+ }\r
+ for(i=0;required[i];i+=2)\r
+ if (strcmp(required[i],xtg->tag)==0 && !xtg->rocurrences[i])\r
+ {\r
+  sprintf(ebuf,"Error: (line %d) Tag <%s> is required within <%s>",xtg->beginl, required[i+1],xtg->tag);\r
+  err_h(ebuf,ectx);\r
+ }\r
+ if (parent && strcmp(parent->tag,"identification")==0)\r
+ {\r
+  if (strcmp(xtg->tag,"format")==0)\r
+  {\r
+   int i;\r
+   for(i=0;format_registry[i];i++) if (memcmp(xtg->begin,format_registry[i],strlen(format_registry[i]))==0) break;\r
+   if (format_registry[i]) xti->format=i;\r
+   else\r
+   {\r
+    char bf[256];\r
+    memcpy(bf,xtg->begin,xtg->end-xtg->begin);\r
+    bf[xtg->end-xtg->begin]=0;\r
+    xti->format=-1;\r
+    sprintf(ebuf,"Warning: (line %d) Unknown format %s.",xtg->beginl,bf);\r
+    err_h(ebuf,ectx);\r
+   }\r
+  }\r
+ }\r
+ if (parent && strcmp(parent->tag,"cover")==0)\r
+ {\r
+ if (strcmp(xtg->tag,"width")==0)\r
+ {\r
+  int i;\r
+  sscanf(xtg->begin,"%d",&i);\r
+  if (i<120)\r
+  {\r
+  sprintf(ebuf,"Warning: (line %d) Cover art width should not be less than 120.",xtg->beginl);\r
+  err_h(ebuf,ectx);\r
+  }\r
+  if (i>1200)\r
+  {\r
+  sprintf(ebuf,"Warning: (line %d) Cover art width should not exceed 1200.",xtg->beginl);\r
+  err_h(ebuf,ectx);\r
+  }\r
+  if (!xti->width) xti->width=i;\r
+  if (xti->height && (xti->width> 2 * xti->height || xti->height > 2 * xti->width))\r
+  {\r
+  sprintf(ebuf,"Warning: (line %d) Cover art aspect ratio exceeds 2:1.",xtg->beginl);\r
+  err_h(ebuf,ectx);\r
+  }\r
+\r
+ }\r
+ if (strcmp(xtg->tag,"height")==0)\r
+ {\r
+  int i;\r
+  sscanf(xtg->begin,"%d",&i);\r
+  if (i<120)\r
+  {\r
+  sprintf(ebuf,"Warning: (line %d) Cover art height should not be less than 120.",xtg->beginl);\r
+  err_h(ebuf,ectx);\r
+  }\r
+  if (i>1200)\r
+  {\r
+  sprintf(ebuf,"Warning: (line %d) Cover art height should not exceed 1200.",xtg->beginl);\r
+  err_h(ebuf,ectx);\r
+  }\r
+  if (!xti->height) xti->height=i;\r
+  if (xti->width && (xti->width> 2 * xti->height || xti->height > 2 * xti->width))\r
+  {\r
+  sprintf(ebuf,"Warning: (line %d) Cover art aspect ratio exceeds 2:1.",xtg->beginl);\r
+  err_h(ebuf,ectx);\r
+  }\r
+\r
+ }\r
+ if (strcmp(xtg->tag,"format")==0 && memcmp(xtg->begin,"jpg",3) && memcmp(xtg->begin,"png",3))\r
+ {\r
+  sprintf(ebuf,"Warning: (line %d) <format> should be one of: png, jpg.",xtg->beginl);\r
+  err_h(ebuf,ectx);\r
+ }\r
+ }\r
+ if (parent && strcmp(parent->tag,"bibliographic")==0)\r
+ {\r
+  char *p;\r
+  if (isspace(*xtg->begin)|| isspace(*(xtg->end-1)))\r
+   {\r
+    sprintf(ebuf,"Warning: (line %d) Extraneous spaces at beginning or end of tag <%s>.",xtg->beginl,xtg->tag);\r
+    err_h(ebuf,ectx);\r
+   }\r
+  for(p=xtg->begin;p<xtg->end-1;p++)\r
+/* Obsoleted by Revision 6\r
+  if (isspace(*p) && isspace(*(p+1)))\r
+  {\r
+  sprintf(ebuf,"Warning: (line %d) Extraneous spaces found in tag <%s>.",xtg->beginl, xtg->tag);\r
+  err_h(ebuf,ectx);\r
+  }\r
+  else if (isspace(*p) && *p!=' ')\r
+  {\r
+  sprintf(ebuf,"Warning: (line %d) Improper whitespace character found in tag <%s>.",xtg->beginl, xtg->tag);\r
+  err_h(ebuf,ectx);\r
+\r
+  }\r
+*/\r
+ if (strcmp(xtg->tag, "description") && xtg->end-xtg->begin > 240)\r
+ { \r
+  sprintf(ebuf,"Warning: (line %d) Tag <%s> length exceeds treaty guidelines",xtg->beginl, xtg->tag);\r
+  err_h(ebuf,ectx);\r
+ }\r
+ if (strcmp(xtg->tag, "description")==0 && xtg->end-xtg->begin > 2400)\r
+ {\r
+  sprintf(ebuf,"Warning: (line %d) Tag <%s> length exceeds treaty guidelines",xtg->beginl, xtg->tag);\r
+  err_h(ebuf,ectx);\r
+ }\r
+ if (strcmp(xtg->tag,"firstpublished")==0)\r
+ {\r
+  int l=xtg->end-xtg->begin;\r
+  if ((l!=4 && l!=10) ||\r
+      (!isdigit(xtg->begin[0]) ||\r
+       !isdigit(xtg->begin[1]) ||\r
+       !isdigit(xtg->begin[2]) ||\r
+       !isdigit(xtg->begin[3])) ||\r
+      (l==10 && ( xtg->begin[4]!='-' ||\r
+                  xtg->begin[7]!='-' ||\r
+                  !isdigit(xtg->begin[5]) ||\r
+                  !isdigit(xtg->begin[6]) ||\r
+                  !(xtg->begin[5]=='0' || xtg->begin[5]=='1') ||\r
+                  !(xtg->begin[5]=='0' || xtg->begin[6]<='2') ||\r
+                  !isdigit(xtg->begin[8]) ||\r
+                  !isdigit(xtg->begin[9]))))\r
+  {\r
+   sprintf(ebuf,"Warning: (line %d) Tag <%s> should be format YYYY or YYYY-MM-DD",xtg->beginl, xtg->tag);\r
+   err_h(ebuf,ectx);\r
+  }\r
+ }\r
+ if (strcmp(xtg->tag,"seriesnumber")==0)\r
+ {\r
+  char *l;\r
+  if (*xtg->begin=='0' && xtg->end!=xtg->begin+1)\r
+  {\r
+   sprintf(ebuf,"Warning: (line %d) Tag <%s> should not use leading zeroes",xtg->beginl, xtg->tag);\r
+   err_h(ebuf,ectx);\r
+  }\r
+\r
+  for(l=xtg->begin;l<xtg->end;l++) if (!isdigit(*l))\r
+  {\r
+   sprintf(ebuf,"Warning: (line %d) Tag <%s> should be a positive number",xtg->beginl, xtg->tag);\r
+   err_h(ebuf,ectx);\r
+  }\r
+ }\r
+ if (strcmp(xtg->tag,"forgiveness")==0)\r
+ {\r
+  int l;\r
+  for(l=0;zarfian[l];l++) if (memcmp(xtg->begin,zarfian[l],strlen(zarfian[l]))==0) break;\r
+  if (!zarfian[l])\r
+  {\r
+   sprintf(ebuf,"Warning: (line %d) <forgiveness> should be one of: Merciful, Polite, Tough, Cruel",xtg->beginl);\r
+   err_h(ebuf,ectx);\r
+  }\r
+ }\r
+ }\r
+ if (xti->format>0)\r
+ { \r
+  for(i=0;format_registry[i];i++) if (strcmp(xtg->tag,format_registry[i])==0) break;\r
+  if (format_registry[i] && xti->format !=i)\r
+  {\r
+  sprintf(ebuf,"Warning: (line %d) Found <%s> tag, but story is identified as %s.",xtg->beginl, xtg->tag, format_registry[xti->format]);\r
+  err_h(ebuf,ectx);\r
+  }\r
+ }\r
+ if (strcmp(xtg->tag,"story")==0)\r
+ {\r
+  xti->format=-1;\r
+  xti->width=0;\r
+  xti->height=0;\r
+ }\r
+\r
+}\r
+\r
+\r
+\r
+void ifiction_parse(char *md, IFCloseTag close_tag, void *close_ctx, IFErrorHandler error_handler, void *error_ctx)\r
+{\r
+char *xml, buffer[2400], *aep, *mda=md, ebuffer[512];\r
+struct XMLTag *parse=NULL, *xtg;\r
+struct ifiction_info xti;\r
+char BOM[3]={ 0xEF, 0xBB, 0xBF};\r
+xti.width=0;\r
+xti.height=0;\r
+xti.format=-1;\r
+llp=1;\r
+lnlst=md;\r
+\r
+while(*mda && isspace(*mda)) mda++;\r
+if (memcmp(mda,BOM,3)==0)\r
+{ mda+=3;\r
+  while(*mda && isspace(*mda)) mda++;\r
+}\r
+\r
+\r
+if (strncmp("<?xml version=\"1.0\" encoding=\"UTF-8\"?>",mda,\r
+        strlen("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"))\r
+    &&\r
+    strncmp("<?xml version=\"1.0\" encoding=\"utf-8\"?>",mda,\r
+        strlen("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"))\r
+   )\r
+{\r
+ error_handler("Error: XML header not found.",error_ctx);\r
+ return;\r
+}\r
+\r
+xml=strstr(md,"<ifindex");\r
+if (!xml) {\r
+ error_handler("Error: <ifindex> not found",error_ctx);\r
+ return;\r
+ }\r
+while(xml && *xml)\r
+{\r
+ char *bp, *ep, *tp;\r
+ while(*xml&&*xml!='<') xml++;\r
+ if (!*xml) break;\r
+ bp=xml;\r
+ tp=strchr(bp+1,'<');\r
+ ep=strchr(bp+1,'>');\r
+ if (!ep) break;\r
+ if (tp && tp < ep)\r
+  { xml=tp; continue; }\r
+ if (!tp) tp=ep+1; \r
+ if (bp[1]=='/') /* end tag */\r
+ {\r
+   strncpy(buffer,bp+2,(ep-bp)-2);\r
+   buffer[(ep-bp)-2]=0;\r
+   if (parse && strcmp(buffer,parse->tag)==0)\r
+   { /* copasetic. Close the tag */\r
+    xtg=parse;\r
+    parse=xtg->next;\r
+    xtg->end=ep-strlen(buffer)-2;\r
+    ifiction_validate_tag(xtg,&xti,error_handler, error_ctx);\r
+    close_tag(xtg,close_ctx);\r
+    free(xtg);\r
+   }\r
+   else\r
+   {\r
+    for(xtg=parse;xtg && strcmp(buffer,xtg->tag);xtg=xtg->next);\r
+    if (xtg) /* Intervening unclosed tags */\r
+    { for(xtg=parse;xtg && strcmp(buffer,parse->tag);xtg=parse)\r
+     {\r
+      xtg->end=xml-1;\r
+      parse=xtg->next;\r
+      sprintf(ebuffer,"Error: (line %d) unclosed <%s> tag",xtg->beginl,xtg->tag);\r
+      error_handler(ebuffer,error_ctx);\r
+      ifiction_validate_tag(xtg,&xti,error_handler, error_ctx);\r
+      close_tag(xtg,close_ctx);\r
+      free(xtg);\r
+     }\r
+     xtg=parse;\r
+     if (xtg)\r
+     {\r
+      xtg->end=xml-1;\r
+      parse=xtg->next;\r
+      ifiction_validate_tag(xtg,&xti, error_handler, error_ctx);\r
+      close_tag(xtg,close_ctx);\r
+      free(xtg);\r
+     }\r
+    }\r
+    else\r
+    { \r
+      sprintf(ebuffer,"Error: (line %d) saw </%s> without <%s>",getln(xml), buffer,buffer);\r
+      error_handler(ebuffer,error_ctx);\r
+    }\r
+   }\r
+\r
+ }\r
+ else if(*(ep-1)=='/' || bp[1]=='!') /* unterminated tag */\r
+ {\r
+  /* Do nothing */\r
+ }\r
+ else /* Terminated tag beginning */\r
+ {\r
+  int i;\r
+  xtg=(struct XMLTag *)my_malloc(sizeof(struct XMLTag),"XML Tag");\r
+  xtg->next=parse;\r
+  xtg->beginl=getln(bp);\r
+  for(i=0;bp[i+1]=='_' || bp[i+1]=='-' || isalnum(bp[i+1]);i++)\r
+   xtg->tag[i]=bp[i+1];\r
+  if (i==0)\r
+  { xml=tp;\r
+    free(xtg);\r
+    continue;\r
+  }\r
+  parse=xtg;\r
+  parse->tag[i]=0;\r
+  strncpy(parse->fulltag,bp+1,ep-bp-1);\r
+  parse->fulltag[ep-bp-1]=0;\r
+  parse->begin=ep+1;\r
+ }\r
+ xml=tp;\r
+}\r
+ while (parse)\r
+ {\r
+      xtg=parse;\r
+      xtg->end=aep-1;\r
+      parse=xtg->next;\r
+      sprintf(ebuffer,"Error: (line %d) Unclosed tag <%s>",xtg->beginl,xtg->tag);\r
+      ifiction_validate_tag(xtg,&xti,error_handler, error_ctx);\r
+      close_tag(xtg,close_ctx);\r
+      free(xtg);\r
+ }\r
+}\r
+\r
+struct get_tag\r
+{\r
+ char *tag;\r
+ char *parent;\r
+ char *output;\r
+ char *target;\r
+};\r
+\r
+static void ifiction_null_eh(char *e, void *c)\r
+{\r
+ if (e || c) { }\r
+\r
+}\r
+\r
+static void ifiction_find_value(struct XMLTag *xtg, void *xti)\r
+{\r
+ struct get_tag *gt=(struct get_tag *)xti;\r
+\r
+ if (gt->output && !gt->target) return;\r
+ if (gt->target && gt->output && strcmp(gt->output,gt->target)==0) { gt->target=NULL; free(gt->output); gt->output=NULL; }\r
+ if (((!xtg->next && !gt->parent) || (xtg->next && gt->parent && strcmp(xtg->next->tag,gt->parent)==0)) &&\r
+      strcmp(xtg->tag,gt->tag)==0)\r
+ {\r
+  int32 l = xtg->end-xtg->begin;\r
+\r
+  if (gt->output) free(gt->output);\r
+  gt->output=(char *)my_malloc(l+1, "ifiction tag buffer");\r
+  memcpy(gt->output, xtg->begin, l);\r
+  gt->output[l]=0;\r
+\r
+ }\r
+}\r
+\r
+\r
+char *ifiction_get_tag(char *md, char *p, char *t, char *from)\r
+{\r
+ struct get_tag gt;\r
+ gt.output=NULL;\r
+ gt.parent=p;\r
+ gt.tag=t;\r
+ gt.target=from;\r
+ ifiction_parse(md,ifiction_find_value,&gt,ifiction_null_eh,NULL);\r
+ if (gt.target){ if (gt.output) free(gt.output); return NULL; }\r
+ return gt.output;\r
+}\r
diff --git a/babel/ifiction.h b/babel/ifiction.h
new file mode 100644 (file)
index 0000000..75ae946
--- /dev/null
@@ -0,0 +1,45 @@
+/* ifiction.h  declarations for the babel ifiction API\r
+ * (c) 2006 By L. Ross Raszewski\r
+ *\r
+ * This code is freely usable for all purposes.\r
+ *\r
+ * This work is licensed under the Creative Commons Attribution2.5 License.\r
+ * To view a copy of this license, visit\r
+ * http://creativecommons.org/licenses/by/2.5/ or send a letter to\r
+ * Creative Commons,\r
+ * 543 Howard Street, 5th Floor,\r
+ * San Francisco, California, 94105, USA.\r
+ *\r
+ */\r
+\r
+#ifndef IFICTION_H\r
+#define IFICTION_H\r
+\r
+#include "treaty.h"\r
+\r
+/* Babel's notion of an XML tag */\r
+struct XMLTag\r
+{\r
+ int32 beginl;                  /* Beginning line number */\r
+ char tag[256];                 /* name of the tag */\r
+ char fulltag[256];             /* Full text of the opening tag */\r
+ char *begin;                   /* Points to the beginning of the tag's content */\r
+ char *end;                     /* Points to the end of the tag's content.\r
+                                   setting *end=0 will turn begin into a string\r
+                                   containing the tag's content (But if you do this, you\r
+                                   should restore the original value of *end before\r
+                                   allowing control to return to the ifiction parser) */\r
+ char occurences[256];          /* Tables used internally to find missing required tags */\r
+ char rocurrences[256];\r
+ struct XMLTag *next;           /* The tag's parent */\r
+\r
+};\r
+\r
+typedef void (*IFCloseTag)(struct XMLTag *, void *);\r
+typedef void (*IFErrorHandler)(char *, void *);\r
+\r
+\r
+void ifiction_parse(char *md, IFCloseTag close_tag, void *close_ctx, IFErrorHandler error_handler, void *error_ctx);\r
+int32 ifiction_get_IFID(char *metadata, char *output, int32 output_extent);\r
+char *ifiction_get_tag(char *md, char *p, char *t, char *from);\r
+#endif\r
diff --git a/babel/level9.c b/babel/level9.c
new file mode 100644 (file)
index 0000000..de855b0
--- /dev/null
@@ -0,0 +1,495 @@
+/* level9.c  Treaty of Babel module for Level 9 files\r
+ * 2006 By L. Ross Raszewski\r
+ *\r
+ * Note that this module will handle both bare Level 9 A-Code and\r
+ * Spectrum .SNA snapshots.  It will not handle compressed .Z80 images.\r
+ *\r
+ * The Level 9 identification algorithm is based in part on the algorithm\r
+ * used by Paul David Doherty's l9cut program.\r
+ *\r
+ * This file depends on treaty_builder.h\r
+ *\r
+ * This file is public domain, but note that any changes to this file\r
+ * may render it noncompliant with the Treaty of Babel\r
+ */\r
+\r
+#define FORMAT level9\r
+#define HOME_PAGE "http://www.if-legends.org/~l9memorial/html/home.html"\r
+#define FORMAT_EXT ".l9,.sna"\r
+#define NO_METADATA\r
+#define NO_COVER\r
+\r
+#include "treaty_builder.h"\r
+#include <ctype.h>\r
+#include <stdio.h>\r
+#include <string.h>\r
+\r
+struct l9rec {\r
+                int32 length;\r
+                unsigned char chk;\r
+                char *ifid;\r
+};\r
+\r
+\r
+static struct l9rec l9_registry[] = {\r
+      { 0x3a31, 0xe5, "LEVEL9-001-1" },\r
+      { 0x8333, 0xb7, "LEVEL9-001-1" },\r
+      { 0x7c6f, 0x0f, "LEVEL9-001-1" },\r
+      { 0x72fa, 0x8b, "LEVEL9-001-1" },\r
+      { 0x38dd, 0x31, "LEVEL9-001-A" },\r
+      { 0x39c0, 0x44, "LEVEL9-001-B" },\r
+      { 0x3a12, 0x8f, "LEVEL9-001-C" },\r
+      { 0x37f1, 0x77, "LEVEL9-001-2" },\r
+      { 0x844d, 0x50, "LEVEL9-001-2" },\r
+      { 0x738e, 0x5b, "LEVEL9-001-2" },\r
+      { 0x3900, 0x1c, "LEVEL9-001-3" },\r
+      { 0x8251, 0x5f, "LEVEL9-001-3" },\r
+      { 0x7375, 0xe5, "LEVEL9-001-3" },\r
+      { 0x3910, 0xac, "LEVEL9-001-4" },\r
+      { 0x7a78, 0x5e, "LEVEL9-001-4" },\r
+      { 0x78d5, 0xe3, "LEVEL9-001-4" },\r
+      { 0x3ad6, 0xa7, "LEVEL9-001-5" },\r
+      { 0x38a5, 0x0f, "LEVEL9-001-6" },\r
+      { 0x361e, 0x7e, "LEVEL9-001-7" },\r
+      { 0x3934, 0x75, "LEVEL9-001-8" },\r
+      { 0x3511, 0xcc, "LEVEL9-001-9" },\r
+      { 0x593a, 0xaf, "LEVEL9-002-1" },\r
+      { 0x7931, 0xb9, "LEVEL9-002-1" },\r
+      { 0x6841, 0x4a, "LEVEL9-002-1" },\r
+      { 0x57e6, 0x8a, "LEVEL9-002-2" },\r
+      { 0x7cdf, 0xa5, "LEVEL9-002-2" },\r
+      { 0x6bc0, 0x62, "LEVEL9-002-2" },\r
+      { 0x5819, 0xcd, "LEVEL9-002-3" },\r
+      { 0x7a0c, 0x97, "LEVEL9-002-3" },\r
+      { 0x692c, 0x21, "LEVEL9-002-3" },\r
+      { 0x579b, 0xad, "LEVEL9-002-4" },\r
+      { 0x7883, 0xe2, "LEVEL9-002-4" },\r
+      { 0x670a, 0x94, "LEVEL9-002-4" },\r
+      { 0x5323, 0xb7, "LEVEL9-003" },\r
+      { 0x6e60, 0x83, "LEVEL9-003" },\r
+      { 0x5b58, 0x50, "LEVEL9-003" },\r
+      { 0x63b6, 0x2e, "LEVEL9-003" },\r
+      { 0x6968, 0x32, "LEVEL9-003" },\r
+      { 0x5b50, 0x66, "LEVEL9-003" },\r
+      { 0x6970, 0xd6, "LEVEL9-003" },\r
+      { 0x5ace, 0x11, "LEVEL9-003" },\r
+      { 0x6e5c, 0xf6, "LEVEL9-003" },\r
+      { 0x1929, 0x00, "LEVEL9-004-DEMO" },\r
+      { 0x40e0, 0x02, "LEVEL9-004-DEMO" },\r
+      { 0x3ebb, 0x00, "LEVEL9-004-en" },\r
+      { 0x3e4f, 0x00, "LEVEL9-004-en" },\r
+      { 0x3e8f, 0x00, "LEVEL9-004-en" },\r
+      { 0x0fd8, 0x00, "LEVEL9-004-en" },\r
+      { 0x14a3, 0x00, "LEVEL9-004-en" },\r
+      { 0x110f, 0x00, "LEVEL9-004-fr" },\r
+      { 0x4872, 0x00, "LEVEL9-004-de" },\r
+      { 0x4846, 0x00, "LEVEL9-004-de" },\r
+      { 0x11f5, 0x00, "LEVEL9-004-de" },\r
+      { 0x11f5, 0x00, "LEVEL9-004-de" },\r
+      { 0x76f4, 0x5e, "LEVEL9-005" },\r
+      { 0x5b16, 0x3b, "LEVEL9-005" },\r
+      { 0x6c8e, 0xb6, "LEVEL9-005" },\r
+      { 0x6f4d, 0xcb, "LEVEL9-005" },\r
+      { 0x6f6a, 0xa5, "LEVEL9-005" },\r
+      { 0x5e31, 0x7c, "LEVEL9-005" },\r
+      { 0x6f70, 0x40, "LEVEL9-005" },\r
+      { 0x6f6e, 0x78, "LEVEL9-005" },\r
+      { 0x5a8e, 0xf2, "LEVEL9-005" },\r
+      { 0x76f4, 0x5a, "LEVEL9-005" },\r
+      { 0x630e, 0x8d, "LEVEL9-006" },\r
+      { 0x630e, 0xbe, "LEVEL9-006" },\r
+      { 0x6f0c, 0x95, "LEVEL9-006" },\r
+      { 0x593a, 0x80, "LEVEL9-006" },\r
+      { 0x6bd2, 0x65, "LEVEL9-006" },\r
+      { 0x6dc0, 0x63, "LEVEL9-006" },\r
+      { 0x58a6, 0x24, "LEVEL9-006" },\r
+      { 0x6de8, 0x4c, "LEVEL9-006" },\r
+      { 0x58a3, 0x38, "LEVEL9-006" },\r
+      { 0x63be, 0xd6, "LEVEL9-007" },\r
+      { 0x378c, 0x8d, "LEVEL9-007" },\r
+      { 0x63be, 0x0a, "LEVEL9-007" },\r
+      { 0x34b3, 0x20, "LEVEL9-008" },\r
+      { 0x34b3, 0xc7, "LEVEL9-008" },\r
+      { 0x34b3, 0x53, "LEVEL9-008" },\r
+      { 0xb1a9, 0x80, "LEVEL9-009-1" },\r
+      { 0x908e, 0x0d, "LEVEL9-009-1" },\r
+      { 0xad41, 0xa8, "LEVEL9-009-1" },\r
+      { 0xb1aa, 0xad, "LEVEL9-009-1" },\r
+      { 0x8aab, 0xc0, "LEVEL9-009-1" },\r
+      { 0xb0ec, 0xc2, "LEVEL9-009-1" },\r
+      { 0xb19e, 0x92, "LEVEL9-009-1" },\r
+      { 0x5ff0, 0xf8, "LEVEL9-009-1" },\r
+      { 0x52aa, 0xdf, "LEVEL9-009-1" },\r
+      { 0xab9d, 0x31, "LEVEL9-009-2" },\r
+      { 0x8f6f, 0x0a, "LEVEL9-009-2" },\r
+      { 0xa735, 0xf7, "LEVEL9-009-2" },\r
+      { 0xab8b, 0xbf, "LEVEL9-009-2" },\r
+      { 0x8ac8, 0x9a, "LEVEL9-009-2" },\r
+      { 0xaf82, 0x83, "LEVEL9-009-2" },\r
+      { 0x6024, 0x01, "LEVEL9-009-2" },\r
+      { 0x6ffa, 0xdb, "LEVEL9-009-2" },\r
+      { 0xae28, 0x87, "LEVEL9-009-3" },\r
+      { 0x9060, 0xbb, "LEVEL9-009-3" },\r
+      { 0xa9c0, 0x9e, "LEVEL9-009-3" },\r
+      { 0xae16, 0x81, "LEVEL9-009-3" },\r
+      { 0x8a93, 0x4f, "LEVEL9-009-3" },\r
+      { 0xb3e6, 0xab, "LEVEL9-009-3" },\r
+      { 0x6036, 0x3d, "LEVEL9-009-3" },\r
+      { 0x723a, 0x69, "LEVEL9-009-3" },\r
+      { 0xd188, 0x13, "LEVEL9-010-1" },\r
+      { 0x9089, 0xce, "LEVEL9-010-1" },\r
+      { 0xb770, 0x03, "LEVEL9-010-1" },\r
+      { 0xd19b, 0xad, "LEVEL9-010-1" },\r
+      { 0x8ab7, 0x68, "LEVEL9-010-1" },\r
+      { 0xd183, 0x83, "LEVEL9-010-1" },\r
+      { 0x5a38, 0xf7, "LEVEL9-010-1" },\r
+      { 0x76a0, 0x3a, "LEVEL9-010-1" },\r
+      { 0xc594, 0x03, "LEVEL9-010-2" },\r
+      { 0x908d, 0x80, "LEVEL9-010-2" },\r
+      { 0xb741, 0xb6, "LEVEL9-010-2" },\r
+      { 0xc5a5, 0xfe, "LEVEL9-010-2" },\r
+      { 0x8b1e, 0x84, "LEVEL9-010-2" },\r
+      { 0xc58f, 0x65, "LEVEL9-010-2" },\r
+      { 0x531a, 0xed, "LEVEL9-010-2" },\r
+      { 0x7674, 0x0b, "LEVEL9-010-2" },\r
+      { 0xd79f, 0xb5, "LEVEL9-010-3" },\r
+      { 0x909e, 0x9f, "LEVEL9-010-3" },\r
+      { 0xb791, 0xa1, "LEVEL9-010-3" },\r
+      { 0xd7ae, 0x9e, "LEVEL9-010-3" },\r
+      { 0x8b1c, 0xa8, "LEVEL9-010-3" },\r
+      { 0xd79a, 0x57, "LEVEL9-010-3" },\r
+      { 0x57e4, 0x19, "LEVEL9-010-3" },\r
+      { 0x765e, 0xba, "LEVEL9-010-3" },\r
+      { 0xbb93, 0x36, "LEVEL9-011-1" },\r
+      { 0x898a, 0x43, "LEVEL9-011-1" },\r
+      { 0x8970, 0x6b, "LEVEL9-011-1" },\r
+      { 0xbb6e, 0xa6, "LEVEL9-011-1" },\r
+      { 0x86d0, 0xb7, "LEVEL9-011-1" },\r
+      { 0xbb6e, 0xad, "LEVEL9-011-1" },\r
+      { 0x46ec, 0x64, "LEVEL9-011-1" },\r
+      { 0x74e0, 0x92, "LEVEL9-011-1" },\r
+      { 0xc58e, 0x4a, "LEVEL9-011-2" },\r
+      { 0x8b9f, 0x61, "LEVEL9-011-2" },\r
+      { 0x8b90, 0x4e, "LEVEL9-011-2" },\r
+      { 0xc58e, 0x43, "LEVEL9-011-2" },\r
+      { 0x8885, 0x22, "LEVEL9-011-2" },\r
+      { 0x6140, 0x18, "LEVEL9-011-2" },\r
+      { 0x6dbc, 0x97, "LEVEL9-011-2" },\r
+      { 0xcb9a, 0x0f, "LEVEL9-011-3" },\r
+      { 0x8af9, 0x61, "LEVEL9-011-3" },\r
+      { 0x8aea, 0x4e, "LEVEL9-011-3" },\r
+      { 0xcb9a, 0x08, "LEVEL9-011-3" },\r
+      { 0x87e5, 0x0e, "LEVEL9-011-3" },\r
+      { 0x640e, 0xc1, "LEVEL9-011-3" },\r
+      { 0x7402, 0x07, "LEVEL9-011-3" },\r
+      { 0xbba4, 0x94, "LEVEL9-012-1" },\r
+      { 0xc0cf, 0x4e, "LEVEL9-012-1" },\r
+      { 0x8afc, 0x07, "LEVEL9-012-1" },\r
+      { 0x8feb, 0xba, "LEVEL9-012-1" },\r
+      { 0xb4c9, 0x94, "LEVEL9-012-1" },\r
+      { 0xc0bd, 0x57, "LEVEL9-012-1" },\r
+      { 0x8ade, 0xf2, "LEVEL9-012-1" },\r
+      { 0x4fd2, 0x9d, "LEVEL9-012-1" },\r
+      { 0x5c7a, 0x44, "LEVEL9-012-1" },\r
+      { 0x768c, 0xe8, "LEVEL9-012-1" },\r
+      { 0xd0c0, 0x56, "LEVEL9-012-2" },\r
+      { 0xd5e9, 0x6a, "LEVEL9-012-2" },\r
+      { 0x8aec, 0x13, "LEVEL9-012-2" },\r
+      { 0x8f6b, 0xfa, "LEVEL9-012-2" },\r
+      { 0xb729, 0x51, "LEVEL9-012-2" },\r
+      { 0xd5d7, 0x99, "LEVEL9-012-2" },\r
+      { 0x8b0e, 0xfb, "LEVEL9-012-2" },\r
+      { 0x4dac, 0xa8, "LEVEL9-012-2" },\r
+      { 0x53a2, 0x1e, "LEVEL9-012-2" },\r
+      { 0x76b0, 0x1d, "LEVEL9-012-2" },\r
+      { 0xb6ac, 0xc6, "LEVEL9-012-3" },\r
+      { 0xbb8f, 0x1a, "LEVEL9-012-3" },\r
+      { 0x8aba, 0x0d, "LEVEL9-012-3" },\r
+      { 0x8f71, 0x2f, "LEVEL9-012-3" },\r
+      { 0xb702, 0xe4, "LEVEL9-012-3" },\r
+      { 0xbb7d, 0x17, "LEVEL9-012-3" },\r
+      { 0x8ab3, 0xc1, "LEVEL9-012-3" },\r
+      { 0x4f96, 0x22, "LEVEL9-012-3" },\r
+      { 0x5914, 0x22, "LEVEL9-012-3" },\r
+      { 0x765e, 0x4f, "LEVEL9-012-3" },\r
+      { 0x5eb9, 0x30, "LEVEL9-013" },\r
+      { 0x5eb9, 0x5d, "LEVEL9-013" },\r
+      { 0x5eb9, 0x6e, "LEVEL9-013" },\r
+      { 0xb257, 0xf8, "LEVEL9-013" },\r
+      { 0xb576, 0x2a, "LEVEL9-013" },\r
+      { 0x8d78, 0x3a, "LEVEL9-013" },\r
+      { 0x9070, 0x43, "LEVEL9-013" },\r
+      { 0xb38c, 0x37, "LEVEL9-013" },\r
+      { 0xb563, 0x6a, "LEVEL9-013" },\r
+      { 0xb57c, 0x44, "LEVEL9-013" },\r
+      { 0xb260, 0xe5, "LEVEL9-013" },\r
+      { 0x8950, 0xa1, "LEVEL9-013" },\r
+      { 0xb579, 0x89, "LEVEL9-013" },\r
+      { 0x579e, 0x97, "LEVEL9-013" },\r
+      { 0x69fe, 0x56, "LEVEL9-013" },\r
+      { 0x6f1e, 0xda, "LEVEL9-013" },\r
+      { 0x5671, 0xbc, "LEVEL9-014" },\r
+      { 0x6fc6, 0x14, "LEVEL9-014" },\r
+      { 0x5aa4, 0xc1, "LEVEL9-014" },\r
+      { 0x7410, 0x5e, "LEVEL9-014" },\r
+      { 0x5aa4, 0xc1, "LEVEL9-014" },\r
+      { 0x5aa4, 0xc1, "LEVEL9-014" },\r
+      { 0xb797, 0x1f, "LEVEL9-014" },\r
+      { 0xbaca, 0x3a, "LEVEL9-014" },\r
+      { 0x8c46, 0xf0, "LEVEL9-014" },\r
+      { 0x8f51, 0xb2, "LEVEL9-014" },\r
+      { 0xb451, 0xa8, "LEVEL9-014" },\r
+      { 0xbab2, 0x87, "LEVEL9-014" },\r
+      { 0xbac7, 0x7f, "LEVEL9-014" },\r
+      { 0xb7a0, 0x7e, "LEVEL9-014" },\r
+      { 0x8a60, 0x2a, "LEVEL9-014" },\r
+      { 0xbac4, 0x80, "LEVEL9-014" },\r
+      { 0x579a, 0x2a, "LEVEL9-014" },\r
+      { 0x5a50, 0xa9, "LEVEL9-014" },\r
+      { 0x6108, 0xdd, "LEVEL9-014" },\r
+      { 0x506c, 0xf0, "LEVEL9-015" },\r
+      { 0x505d, 0x32, "LEVEL9-015" },\r
+      { 0xa398, 0x82, "LEVEL9-015" },\r
+      { 0xa692, 0xd1, "LEVEL9-015" },\r
+      { 0x8d56, 0xd3, "LEVEL9-015" },\r
+      { 0x903f, 0x6b, "LEVEL9-015" },\r
+      { 0xa4e2, 0xa6, "LEVEL9-015" },\r
+      { 0xa67c, 0xb8, "LEVEL9-015" },\r
+      { 0xa69e, 0x6c, "LEVEL9-015" },\r
+      { 0xa3a4, 0xdf, "LEVEL9-015" },\r
+      { 0x8813, 0x11, "LEVEL9-015" },\r
+      { 0xa698, 0x41, "LEVEL9-015" },\r
+      { 0x5500, 0x50, "LEVEL9-015" },\r
+      { 0x6888, 0x8d, "LEVEL9-015" },\r
+      { 0x6da0, 0xb8, "LEVEL9-015" },\r
+      { 0x6064, 0xbd, "LEVEL9-016" },\r
+      { 0x6064, 0x01, "LEVEL9-016" },\r
+      { 0x6047, 0x6c, "LEVEL9-016" },\r
+      { 0x6064, 0xda, "LEVEL9-016" },\r
+      { 0x6064, 0x95, "LEVEL9-016" },\r
+      { 0x60c4, 0x28, "LEVEL9-016" },\r
+      { 0x5cb7, 0xfe, "LEVEL9-016" },\r
+      { 0x5ca1, 0x33, "LEVEL9-016" },\r
+      { 0x5cb7, 0x64, "LEVEL9-016" },\r
+      { 0x7d16, 0xe6, "LEVEL9-016" },\r
+      { 0x639c, 0x8b, "LEVEL9-016" },\r
+      { 0x60f7, 0x68, "LEVEL9-016" },\r
+      { 0x772f, 0xca, "LEVEL9-016" },\r
+      { 0x7cff, 0xf8, "LEVEL9-016" },\r
+      { 0x7cf8, 0x24, "LEVEL9-016" },\r
+      { 0x7d14, 0xe8, "LEVEL9-016" },\r
+      { 0x7c55, 0x18, "LEVEL9-016" },\r
+      { 0x5f43, 0xca, "LEVEL9-016" },\r
+      { 0xc132, 0x14, "LEVEL9-017-1" },\r
+      { 0xbeab, 0x2d, "LEVEL9-017-1" },\r
+      { 0x9058, 0xcf, "LEVEL9-017-1" },\r
+      { 0xbe94, 0xcc, "LEVEL9-017-1" },\r
+      { 0x8a21, 0xf4, "LEVEL9-017-1" },\r
+      { 0x55ce, 0xa1, "LEVEL9-017-1" },\r
+      { 0x5cbc, 0xa5, "LEVEL9-017-1" },\r
+      { 0x762e, 0x82, "LEVEL9-017-1" },\r
+      { 0x99bd, 0x65, "LEVEL9-017-2" },\r
+      { 0x8f43, 0xc9, "LEVEL9-017-2" },\r
+      { 0x8a12, 0xe3, "LEVEL9-017-2" },\r
+      { 0x54a6, 0xa9, "LEVEL9-017-2" },\r
+      { 0x5932, 0x4e, "LEVEL9-017-2" },\r
+      { 0x5bd6, 0x35, "LEVEL9-017-2" },\r
+      { 0xbcb6, 0x7a, "LEVEL9-017-3 (Amiga/PC/ST)" },\r
+      { 0x90ac, 0x68, "LEVEL9-017-3" },\r
+      { 0x8a16, 0xcc, "LEVEL9-017-3" },\r
+      { 0x51bc, 0xe3, "LEVEL9-017-3" },\r
+      { 0x5860, 0x95, "LEVEL9-017-3" },\r
+      { 0x6fa8, 0xa4, "LEVEL9-017-3" },\r
+      { 0x5fab, 0x5c, "LEVEL9-018" },\r
+      { 0x5fab, 0x2f, "LEVEL9-018" },\r
+      { 0x7b31, 0x6e, "LEVEL9-018" },\r
+      { 0x67a3, 0x9d, "LEVEL9-018" },\r
+      { 0x6bf8, 0x3f, "LEVEL9-018" },\r
+      { 0x7363, 0x65, "LEVEL9-018" },\r
+      { 0x7b2f, 0x70, "LEVEL9-018" },\r
+      { 0x7b2f, 0x70, "LEVEL9-018" },\r
+      { 0x6541, 0x02, "LEVEL9-018" },\r
+      { 0x5834, 0x42, "LEVEL9-019-1" },\r
+      { 0x765d, 0xcd, "LEVEL9-019-1" },\r
+      { 0x6ce5, 0x58, "LEVEL9-019-1" },\r
+      { 0x56dd, 0x51, "LEVEL9-019-2" },\r
+      { 0x6e58, 0x07, "LEVEL9-019-2" },\r
+      { 0x68da, 0xc1, "LEVEL9-019-2" },\r
+      { 0x5801, 0x53, "LEVEL9-019-3" },\r
+      { 0x7e98, 0x6a, "LEVEL9-019-3" },\r
+      { 0x6c67, 0x9a, "LEVEL9-019-3" },\r
+      { 0x54a4, 0x01, "LEVEL9-019-4" },\r
+      { 0x81e2, 0xd5, "LEVEL9-019-4" },\r
+      { 0x6d91, 0xb9, "LEVEL9-019-4" },\r
+      { 0x5828, 0xbd, "LEVEL9-020" },\r
+      { 0x6d84, 0xf9, "LEVEL9-020" },\r
+      { 0x6d84, 0xc8, "LEVEL9-020" },\r
+      { 0x6030, 0x47, "LEVEL9-020" },\r
+      { 0x772b, 0xcd, "LEVEL9-020" },\r
+      { 0x546c, 0xb7, "LEVEL9-020" },\r
+      { 0x7cd9, 0x0c, "LEVEL9-020" },\r
+      { 0x60dd, 0xf2, "LEVEL9-020" },\r
+      { 0x6161, 0xf3, "LEVEL9-020" },\r
+      { 0x788d, 0x72, "LEVEL9-020" },\r
+      { 0x7cd7, 0x0e, "LEVEL9-020" },\r
+      { 0x5ebb, 0xf1, "LEVEL9-020" },\r
+\r
+      { 0, 0, NULL }\r
+};\r
+\r
+\r
+\r
+static int32 read_l9_int(unsigned char *sf)\r
+{\r
+ return ((int32) sf[1]) << 8 | sf[0];\r
+\r
+}\r
+static int v2_recognition (unsigned char *sf, int32 extent, int32 *l, unsigned char *c)\r
+{\r
+  int32 i, j;\r
+  for (i=0;i<extent-20;i++)\r
+    if ((read_l9_int(sf+i+4) == 0x0020) &&\r
+        (read_l9_int(sf+i+0x0a) == 0x8000) &&\r
+        (read_l9_int(sf+i+0x14) == read_l9_int(sf+i+0x16)))\r
+    {\r
+      *l=read_l9_int(sf+i+0x1c);\r
+      if (*l && *l+i <=extent)\r
+       {\r
+         *c=0;\r
+         for(j=0;j<=*l;j++)\r
+          *c+=sf[i+j];\r
+         return 2;\r
+       }\r
+    }\r
+ return 0;\r
+}\r
+static int v1_recognition(unsigned char *sf, int32 extent, char **ifid)\r
+{\r
+  int32 i;\r
+  unsigned char a = 0xff, b = 0xff;\r
+\r
+  for (i=0;i<extent-20;i++)\r
+   if (memcmp(sf+i,"ATTAC",5)==0 && sf[i+5]==0xcb)\r
+   {   \r
+    a = sf[i+6];\r
+    break;\r
+   }\r
+  for (;i<(extent-20);i++)\r
+   if (memcmp(sf+i,"BUNC",4)==0 && sf[i+4]==0xc8)\r
+   {\r
+    b = sf[i + 5];\r
+    break;\r
+   }\r
+  if (a == 0xff && b == 0xff)\r
+   return 0;\r
+  if (a == 0x14 && b == 0xff) *ifid="LEVEL9-006";\r
+  else if (a == 0x15 && b == 0x5d) *ifid="LEVEL9-013";\r
+  else if (a == 0x1a && b == 0x24) *ifid="LEVEL9-005";\r
+  else if (a == 0x20 && b == 0x3b) *ifid="LEVEL9-003";\r
+  else *ifid=NULL;\r
+  return 1;\r
+}\r
+static int v3_recognition_phase (int phase,unsigned char *sf, int32 extent, int32 *l, unsigned char *c)\r
+{\r
+  int32 end, i, j, ll;\r
+  ll=0;\r
+  for (i=0;i<extent-20;i++)\r
+  {\r
+    if (ll) break;\r
+    *l = read_l9_int(sf+i);\r
+    end=*l+i;\r
+    if (phase!=3)\r
+    {\r
+    if (end <= (extent - 2) &&\r
+       (\r
+        ((phase == 2) ||\r
+        (((sf[end-1] == 0) &&\r
+         (sf[end-2] == 0)) ||\r
+        ((sf[end+1] == 0) &&\r
+         (sf[end+2] == 0))))\r
+        && (*l>0x4000) && (*l<=0xdb00)))\r
+      if ((*l!=0) && (sf[i+0x0d] == 0))\r
+       for (j=i;j<i+16;j+=2)\r
+        if (((read_l9_int(sf+j)+read_l9_int(sf+j+2))==read_l9_int(sf+j+4))\r
+            && ((read_l9_int(sf+j)+read_l9_int(sf+j+2))))\r
+        ll++;\r
+     }\r
+     else\r
+     {\r
+      if ((extent>0x0fd0) && (end <= (extent - 2)) &&\r
+         (((read_l9_int(sf+i+2) + read_l9_int(sf+i+4))==read_l9_int(sf+i+6))\r
+                    && (read_l9_int(sf+i+2) != 0) && (read_l9_int(sf+i+4)) != 0) &&\r
+         (((read_l9_int(sf+i+6) + read_l9_int(sf+i+8)) == read_l9_int(sf+i+10))\r
+          && ((sf[i + 18] == 0x2a) || (sf[i + 18] == 0x2c))\r
+          && (sf[i + 19] == 0) && (sf[i + 20] == 0) && (sf[i + 21] == 0)))\r
+        ll = 2;\r
+     }\r
+    if (ll>1)\r
+    {\r
+     *c=0;\r
+     if (phase==3) ll=1;\r
+     else\r
+     { char checksum=0;\r
+      *c = sf[end];\r
+      for (j=i;j<=end;j++)\r
+       checksum += sf[j];\r
+      if (!checksum) ll=1;\r
+      else ll=0;\r
+     }\r
+     } else ll=0;\r
+   }\r
+\r
+  if (ll) return *l < 0x8500 ? 3:4;\r
+  return 0;\r
+}\r
+static char *get_l9_ifid(int32 length, unsigned char chk)\r
+{\r
+ int i;\r
+ for(i=0;l9_registry[i].length;i++)\r
+  if (length==l9_registry[i].length && chk==l9_registry[i].chk) return l9_registry[i].ifid;\r
+ return NULL;\r
+}\r
+static int get_l9_version(unsigned char *sf, int32 extent, char **ifid)\r
+{\r
+ int i;\r
+ int32 l;\r
+ unsigned char c;\r
+ if (v2_recognition(sf,extent, &l, &c)) { *ifid=get_l9_ifid(l,c); return 2; }\r
+ l=0; c=0;\r
+ i=v3_recognition_phase(1,sf,extent, &l, &c);\r
+ if (i) { *ifid=get_l9_ifid(l,c); return i; }\r
+ if (v1_recognition(sf,extent, ifid)) return 1;\r
+ l=0; c=0;\r
+ i=v3_recognition_phase(2,sf,extent, &l, &c);\r
+ if (i) { *ifid=get_l9_ifid(l,c); return i; }\r
+ i=v3_recognition_phase(3,sf,extent, &l, &c);\r
+ *ifid=NULL;\r
+ return i;\r
+}\r
+\r
+static int32 claim_story_file(void *story, int32 extent)\r
+{\r
+ char *ifid=NULL;\r
+ if (get_l9_version((unsigned char *) story,extent, &ifid))\r
+  if (ifid) return VALID_STORY_FILE_RV;\r
+  else return NO_REPLY_RV;\r
+ return INVALID_STORY_FILE_RV; \r
+}\r
+\r
+\r
+\r
+static int32 get_story_file_IFID(void *story_file, int32 extent, char *output, int32 output_extent)\r
+{\r
+ char *ifid=NULL;\r
+ int i=get_l9_version((unsigned char *)story_file, extent, &ifid);\r
+ if (!i) return INVALID_STORY_FILE_RV;\r
+ if (ifid)\r
+ {\r
+   ASSERT_OUTPUT_SIZE((signed) strlen(ifid)+1);\r
+   strcpy(output,ifid);\r
+   return 1;\r
+ }\r
+ ASSERT_OUTPUT_SIZE(10);\r
+ sprintf(output,"LEVEL9-%d-",i);\r
+ return INCOMPLETE_REPLY_RV;\r
+}\r
diff --git a/babel/magscrolls.c b/babel/magscrolls.c
new file mode 100644 (file)
index 0000000..c21c922
--- /dev/null
@@ -0,0 +1,124 @@
+/* magscrolls.c  Treaty of Babel module for Z-code files\r
+ * 2006 By L. Ross Raszewski\r
+ *\r
+ * This file depends on treaty_builder.h\r
+ *\r
+ * This file is public domain, but note that any changes to this file\r
+ * may render it noncompliant with the Treaty of Babel\r
+ */\r
+\r
+#define FORMAT magscrolls\r
+#define HOME_PAGE "http://www.if-legends.org/~msmemorial/memorial.htm"\r
+#define FORMAT_EXT ".mag"\r
+#define NO_COVER\r
+#define NO_METADATA\r
+\r
+#include "treaty_builder.h"\r
+#include <ctype.h>\r
+#include <stdio.h>\r
+\r
+struct maginfo\r
+{\r
+  int gv;\r
+  char header[21];\r
+  char *title;\r
+  int bafn;\r
+  int year;\r
+  char *ifid;\r
+  char *author;\r
+  char *meta;\r
+};\r
+\r
+\r
+static struct maginfo manifest[] = {\r
+        { 0, "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",\r
+          "The Pawn",\r
+          0,\r
+          1985,\r
+          "MAGNETIC-1",\r
+          "Rob Steggles",\r
+        },\r
+        { 1, "\000\004\000\001\007\370\000\000\340\000\000\000\041\064\000\000\040\160\000\000",\r
+          "Guild of Thieves",\r
+          0,\r
+          1987,\r
+          "MAGNETIC-2",\r
+          "Rob Steggles",\r
+        },\r
+        { 2, "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000",\r
+          "Jinxter",\r
+          0,\r
+          1987,\r
+          "MAGNETIC-3",\r
+          "Georgina Sinclair and Michael Bywater",\r
+        },\r
+        { 4, "\000\004\000\001\045\140\000\001\000\000\000\000\161\017\000\000\035\210\000\001",\r
+          "Corruption",\r
+          0,\r
+          1988,\r
+          "MAGNETIC-4",\r
+          "Rob Steggles and Hugh Steers",\r
+        },\r
+        { 4, "\000\004\000\001\044\304\000\001\000\000\000\000\134\137\000\000\040\230\000\001",\r
+          "Fish!",\r
+          0,\r
+          1988,\r
+          "MAGNETIC-5",\r
+          "John Molloy, Pete Kemp, Phil South, Rob Steggles",\r
+        },\r
+        { 4, "\000\003\000\000\377\000\000\000\340\000\000\000\221\000\000\000\036\000\000\001",\r
+          "Corruption",\r
+          0,\r
+          1988,\r
+          "MAGNETIC-4",\r
+          "Rob Steggles and Hugh Steers",\r
+        },\r
+        { 4, "\000\003\000\001\000\000\000\000\340\000\000\000\175\000\000\000\037\000\000\001",\r
+          "Fish!",\r
+          0,\r
+          1988,\r
+          "MAGNETIC-5",\r
+          "John Molloy, Pete Kemp, Phil South, Rob Steggles",\r
+        },\r
+        { 4, "\000\003\000\000\335\000\000\000\140\000\000\000\064\000\000\000\023\000\000\000",\r
+          "Myth",\r
+          0,\r
+          1989,\r
+          "MAGNETIC-6",\r
+          "Paul Findley",\r
+        },\r
+        { 4, "\000\004\000\001\122\074\000\001\000\000\000\000\114\146\000\000\057\240\000\001",\r
+          "Wonderland",\r
+          0,\r
+          1990,\r
+          "MAGNETIC-7",\r
+          "David Bishop",\r
+        },\r
+        { 0, "0", NULL, 0, 0, NULL, NULL }\r
+        };\r
+\r
+static int32 get_story_file_IFID(void *story_file, int32 extent, char *output, int32 output_extent)\r
+{\r
+ int i;\r
+ unsigned char *sf=(unsigned char *)story_file;\r
+ if (extent < 42) return INVALID_STORY_FILE_RV;\r
+\r
+ for(i=0;manifest[i].title;i++)\r
+  if ((sf[13]<3 && manifest[i].gv==sf[13]) || memcmp(manifest[i].header,sf+12,20)==0)\r
+   {\r
+    ASSERT_OUTPUT_SIZE(((int32) strlen(manifest[i].ifid)+1));\r
+    strcpy(output,manifest[i].ifid);\r
+    return 1;\r
+   }\r
+ strcpy(output,"MAGNETIC-");\r
+ return INCOMPLETE_REPLY_RV;\r
+}\r
+\r
+static int32 claim_story_file(void *story_file, int32 extent)\r
+{\r
+ if (extent<42 ||\r
+     memcmp(story_file,"MaSc",4)\r
+    ) return INVALID_STORY_FILE_RV;\r
+ return VALID_STORY_FILE_RV;\r
+}\r
+\r
diff --git a/babel/md5.c b/babel/md5.c
new file mode 100644 (file)
index 0000000..c35d96c
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+  Copyright (C) 1999, 2000, 2002 Aladdin Enterprises.  All rights reserved.
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  L. Peter Deutsch
+  ghost@aladdin.com
+
+ */
+/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */
+/*
+  Independent implementation of MD5 (RFC 1321).
+
+  This code implements the MD5 Algorithm defined in RFC 1321, whose
+  text is available at
+       http://www.ietf.org/rfc/rfc1321.txt
+  The code is derived from the text of the RFC, including the test suite
+  (section A.5) but excluding the rest of Appendix A.  It does not include
+  any code or documentation that is identified in the RFC as being
+  copyrighted.
+
+  The original and principal author of md5.c is L. Peter Deutsch
+  <ghost@aladdin.com>.  Other authors are noted in the change history
+  that follows (in reverse chronological order):
+
+  2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+       either statically or dynamically; added missing #include <string.h>
+       in library.
+  2002-03-11 lpd Corrected argument list for main(), and added int return
+       type, in test program and T value program.
+  2002-02-21 lpd Added missing #include <stdio.h> in test program.
+  2000-07-03 lpd Patched to eliminate warnings about "constant is
+       unsigned in ANSI C, signed in traditional"; made test program
+       self-checking.
+  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+  1999-05-03 lpd Original version.
+ */
+
+#include "md5.h"
+#include <string.h>
+
+#undef BYTE_ORDER      /* 1 = big-endian, -1 = little-endian, 0 = unknown */
+#ifdef ARCH_IS_BIG_ENDIAN
+#  define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
+#else
+#  define BYTE_ORDER 0
+#endif
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3    0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6    0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9    0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13    0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16    0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19    0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22    0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25    0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28    0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31    0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35    0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38    0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41    0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44    0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47    0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50    0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53    0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57    0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60    0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63    0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+    md5_word_t
+       a = pms->abcd[0], b = pms->abcd[1],
+       c = pms->abcd[2], d = pms->abcd[3];
+    md5_word_t t;
+#if BYTE_ORDER > 0
+    /* Define storage only for big-endian CPUs. */
+    md5_word_t X[16];
+#else
+    /* Define storage for little-endian or both types of CPUs. */
+    md5_word_t xbuf[16];
+    const md5_word_t *X;
+#endif
+
+    {
+#if BYTE_ORDER == 0
+       /*
+        * Determine dynamically whether this is a big-endian or
+        * little-endian machine, since we can use a more efficient
+        * algorithm on the latter.
+        */
+       static const int w = 1;
+
+       if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
+#endif
+#if BYTE_ORDER <= 0            /* little-endian */
+       {
+           /*
+            * On little-endian machines, we can process properly aligned
+            * data without copying it.
+            */
+           if (!((data - (const md5_byte_t *)0) & 3)) {
+               /* data are properly aligned */
+               X = (const md5_word_t *)data;
+           } else {
+               /* not aligned */
+               memcpy(xbuf, data, 64);
+               X = xbuf;
+           }
+       }
+#endif
+#if BYTE_ORDER == 0
+       else                    /* dynamic big-endian */
+#endif
+#if BYTE_ORDER >= 0            /* big-endian */
+       {
+           /*
+            * On big-endian machines, we must arrange the bytes in the
+            * right order.
+            */
+           const md5_byte_t *xp = data;
+           int i;
+
+#  if BYTE_ORDER == 0
+           X = xbuf;           /* (dynamic only) */
+#  else
+#    define xbuf X             /* (static only) */
+#  endif
+           for (i = 0; i < 16; ++i, xp += 4)
+               xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+       }
+#endif
+    }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+    /* Round 1. */
+    /* Let [abcd k s i] denote the operation
+       a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + F(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+    /* Do the following 16 operations. */
+    SET(a, b, c, d,  0,  7,  T1);
+    SET(d, a, b, c,  1, 12,  T2);
+    SET(c, d, a, b,  2, 17,  T3);
+    SET(b, c, d, a,  3, 22,  T4);
+    SET(a, b, c, d,  4,  7,  T5);
+    SET(d, a, b, c,  5, 12,  T6);
+    SET(c, d, a, b,  6, 17,  T7);
+    SET(b, c, d, a,  7, 22,  T8);
+    SET(a, b, c, d,  8,  7,  T9);
+    SET(d, a, b, c,  9, 12, T10);
+    SET(c, d, a, b, 10, 17, T11);
+    SET(b, c, d, a, 11, 22, T12);
+    SET(a, b, c, d, 12,  7, T13);
+    SET(d, a, b, c, 13, 12, T14);
+    SET(c, d, a, b, 14, 17, T15);
+    SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+     /* Round 2. */
+     /* Let [abcd k s i] denote the operation
+          a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + G(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  1,  5, T17);
+    SET(d, a, b, c,  6,  9, T18);
+    SET(c, d, a, b, 11, 14, T19);
+    SET(b, c, d, a,  0, 20, T20);
+    SET(a, b, c, d,  5,  5, T21);
+    SET(d, a, b, c, 10,  9, T22);
+    SET(c, d, a, b, 15, 14, T23);
+    SET(b, c, d, a,  4, 20, T24);
+    SET(a, b, c, d,  9,  5, T25);
+    SET(d, a, b, c, 14,  9, T26);
+    SET(c, d, a, b,  3, 14, T27);
+    SET(b, c, d, a,  8, 20, T28);
+    SET(a, b, c, d, 13,  5, T29);
+    SET(d, a, b, c,  2,  9, T30);
+    SET(c, d, a, b,  7, 14, T31);
+    SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+     /* Round 3. */
+     /* Let [abcd k s t] denote the operation
+          a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + H(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  5,  4, T33);
+    SET(d, a, b, c,  8, 11, T34);
+    SET(c, d, a, b, 11, 16, T35);
+    SET(b, c, d, a, 14, 23, T36);
+    SET(a, b, c, d,  1,  4, T37);
+    SET(d, a, b, c,  4, 11, T38);
+    SET(c, d, a, b,  7, 16, T39);
+    SET(b, c, d, a, 10, 23, T40);
+    SET(a, b, c, d, 13,  4, T41);
+    SET(d, a, b, c,  0, 11, T42);
+    SET(c, d, a, b,  3, 16, T43);
+    SET(b, c, d, a,  6, 23, T44);
+    SET(a, b, c, d,  9,  4, T45);
+    SET(d, a, b, c, 12, 11, T46);
+    SET(c, d, a, b, 15, 16, T47);
+    SET(b, c, d, a,  2, 23, T48);
+#undef SET
+
+     /* Round 4. */
+     /* Let [abcd k s t] denote the operation
+          a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+  t = a + I(b,c,d) + X[k] + Ti;\
+  a = ROTATE_LEFT(t, s) + b
+     /* Do the following 16 operations. */
+    SET(a, b, c, d,  0,  6, T49);
+    SET(d, a, b, c,  7, 10, T50);
+    SET(c, d, a, b, 14, 15, T51);
+    SET(b, c, d, a,  5, 21, T52);
+    SET(a, b, c, d, 12,  6, T53);
+    SET(d, a, b, c,  3, 10, T54);
+    SET(c, d, a, b, 10, 15, T55);
+    SET(b, c, d, a,  1, 21, T56);
+    SET(a, b, c, d,  8,  6, T57);
+    SET(d, a, b, c, 15, 10, T58);
+    SET(c, d, a, b,  6, 15, T59);
+    SET(b, c, d, a, 13, 21, T60);
+    SET(a, b, c, d,  4,  6, T61);
+    SET(d, a, b, c, 11, 10, T62);
+    SET(c, d, a, b,  2, 15, T63);
+    SET(b, c, d, a,  9, 21, T64);
+#undef SET
+
+     /* Then perform the following additions. (That is increment each
+        of the four registers by the value it had before this block
+        was started.) */
+    pms->abcd[0] += a;
+    pms->abcd[1] += b;
+    pms->abcd[2] += c;
+    pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+    pms->count[0] = pms->count[1] = 0;
+    pms->abcd[0] = 0x67452301;
+    pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+    pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+    pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+    const md5_byte_t *p = data;
+    int left = nbytes;
+    int offset = (pms->count[0] >> 3) & 63;
+    md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+    if (nbytes <= 0)
+       return;
+
+    /* Update the message length. */
+    pms->count[1] += nbytes >> 29;
+    pms->count[0] += nbits;
+    if (pms->count[0] < nbits)
+       pms->count[1]++;
+
+    /* Process an initial partial block. */
+    if (offset) {
+       int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+       memcpy(pms->buf + offset, p, copy);
+       if (offset + copy < 64)
+           return;
+       p += copy;
+       left -= copy;
+       md5_process(pms, pms->buf);
+    }
+
+    /* Process full blocks. */
+    for (; left >= 64; p += 64, left -= 64)
+       md5_process(pms, p);
+
+    /* Process a final partial block. */
+    if (left)
+       memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+    static const md5_byte_t pad[64] = {
+       0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+    };
+    md5_byte_t data[8];
+    int i;
+
+    /* Save the length before padding. */
+    for (i = 0; i < 8; ++i)
+       data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+    /* Pad to 56 bytes mod 64. */
+    md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+    /* Append the length. */
+    md5_append(pms, data, 8);
+    for (i = 0; i < 16; ++i)
+       digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
diff --git a/babel/md5.h b/babel/md5.h
new file mode 100644 (file)
index 0000000..698c995
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+  Copyright (C) 1999, 2002 Aladdin Enterprises.  All rights reserved.
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  L. Peter Deutsch
+  ghost@aladdin.com
+
+ */
+/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */
+/*
+  Independent implementation of MD5 (RFC 1321).
+
+  This code implements the MD5 Algorithm defined in RFC 1321, whose
+  text is available at
+       http://www.ietf.org/rfc/rfc1321.txt
+  The code is derived from the text of the RFC, including the test suite
+  (section A.5) but excluding the rest of Appendix A.  It does not include
+  any code or documentation that is identified in the RFC as being
+  copyrighted.
+
+  The original and principal author of md5.h is L. Peter Deutsch
+  <ghost@aladdin.com>.  Other authors are noted in the change history
+  that follows (in reverse chronological order):
+
+  2002-04-13 lpd Removed support for non-ANSI compilers; removed
+       references to Ghostscript; clarified derivation from RFC 1321;
+       now handles byte order either statically or dynamically.
+  1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+  1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+       added conditionalization for C++ compilation from Martin
+       Purschke <purschke@bnl.gov>.
+  1999-05-03 lpd Original version.
+ */
+
+#ifndef md5_INCLUDED
+#  define md5_INCLUDED
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order.  If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+    md5_word_t count[2];       /* message length in bits, lsw first */
+    md5_word_t abcd[4];                /* digest buffer */
+    md5_byte_t buf[64];                /* accumulate block */
+} md5_state_t;
+
+#ifdef __cplusplus
+extern "C" 
+{
+#endif
+
+/* Initialize the algorithm. */
+void md5_init(md5_state_t *pms);
+
+/* Append a string to the message. */
+void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+
+/* Finish the message and return the digest. */
+void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+#ifdef __cplusplus
+}  /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
diff --git a/babel/misc.c b/babel/misc.c
new file mode 100644 (file)
index 0000000..982927f
--- /dev/null
@@ -0,0 +1,19 @@
+/* misc.h : miscellany for babel\r
+ * This file is public domain\r
+ * 2006 by L. Ross Raszewski\r
+ */\r
+\r
+#include <stdlib.h>\r
+#include <stdio.h>\r
+\r
+void *my_malloc(int size, char *rs)\r
+{\r
+ void *buf=calloc(size,1);\r
+ if (size && !buf)\r
+  {\r
+        fprintf(stderr,"Error: Memory exceeded (%d for %s)!\n",size,rs);\r
+        exit(2);\r
+   }\r
+   return buf;\r
+}\r
+\r
diff --git a/babel/modules.h b/babel/modules.h
new file mode 100644 (file)
index 0000000..cc799ee
--- /dev/null
@@ -0,0 +1,70 @@
+/* modules.h  Declaration of treaty modules for the babel program\r
+ * (c) 2006 By L. Ross Raszewski\r
+ *\r
+ * This code is freely usable for all purposes.\r
+ *\r
+ * This work is licensed under the Creative Commons Attribution2.5 License.\r
+ * To view a copy of this license, visit\r
+ * http://creativecommons.org/licenses/by/2.5/ or send a letter to\r
+ * Creative Commons,\r
+ * 543 Howard Street, 5th Floor,\r
+ * San Francisco, California, 94105, USA.\r
+ *\r
+ * This file depends upon treaty.h and all the references treaty modules\r
+ *\r
+ * Persons wishing to add support for a new module to babel need only\r
+ * add a line in the form below.  New modules should be positioned according\r
+ * to their popularity.  If this file is being used in tandem with register.c\r
+ * (as it is in babel), then being dishonest about the popularity of an added\r
+ * system will make the program non-compliant with the treaty of Babel\r
+ *\r
+ * REGISTER_NAME is used as a placeholder for formats which are specified\r
+ * as existing by the treaty but for which no handler yet exists.\r
+ * remove the REGISTER_NAME for any format which has a registered treaty.\r
+ */\r
+\r
+\r
+#include "treaty.h"\r
+#undef REGISTER_TREATY\r
+#undef REGISTER_CONTAINER\r
+#undef REGISTER_NAME\r
+#ifdef TREATY_REGISTER\r
+#ifdef CONTAINER_REGISTER\r
+#ifdef FORMAT_REGISTER\r
+#define REGISTER_TREATY(x)        #x,\r
+#define REGISTER_NAME(x)          #x,\r
+#define REGISTER_CONTAINER(x)\r
+#else\r
+#define REGISTER_TREATY(x)\r
+#define REGISTER_CONTAINER(x)     x##_treaty,\r
+#define REGISTER_NAME(x)\r
+#endif\r
+#else\r
+#define REGISTER_TREATY(x)        x##_treaty,\r
+#define REGISTER_CONTAINER(x)\r
+#define REGISTER_NAME(x)\r
+#endif\r
+#else\r
+#define REGISTER_TREATY(x)        int32 x##_treaty(int32, void *, int32, void *, int32);\r
+#define REGISTER_CONTAINER(x)        int32 x##_treaty(int32, void *, int32, void *, int32);\r
+#define REGISTER_NAME(x)\r
+#endif\r
+\r
+\r
+REGISTER_CONTAINER(blorb)\r
+REGISTER_TREATY(zcode)\r
+REGISTER_TREATY(glulx)\r
+REGISTER_TREATY(tads2)\r
+REGISTER_TREATY(tads3)\r
+REGISTER_TREATY(hugo)\r
+REGISTER_TREATY(alan)\r
+REGISTER_TREATY(adrift)\r
+REGISTER_TREATY(level9)\r
+REGISTER_TREATY(agt)\r
+REGISTER_TREATY(magscrolls)\r
+REGISTER_TREATY(advsys)\r
+REGISTER_TREATY(executable)\r
+\r
+\r
+\r
+\r
diff --git a/babel/modules.h.gch b/babel/modules.h.gch
new file mode 100644 (file)
index 0000000..99db82c
Binary files /dev/null and b/babel/modules.h.gch differ
diff --git a/babel/register.c b/babel/register.c
new file mode 100644 (file)
index 0000000..22bbed5
--- /dev/null
@@ -0,0 +1,36 @@
+/* register.c    Register modules for the babel handler api\r
+ *\r
+ * 2006 by L. Ross Raszewski\r
+ *\r
+ * This code is freely usable for all purposes.\r
+ *\r
+ * This work is licensed under the Creative Commons Attribution2.5 License.\r
+ * To view a copy of this license, visit\r
+ * http://creativecommons.org/licenses/by/2.5/ or send a letter to\r
+ * Creative Commons,\r
+ * 543 Howard Street, 5th Floor,\r
+ * San Francisco, California, 94105, USA.\r
+ *\r
+ * This file depends on modules.h\r
+ *\r
+ * The purpose of this file is to create the treaty_registry array.\r
+ * This array is a null-terminated list of the known treaty modules.\r
+ */\r
+\r
+#include <stdlib.h>\r
+#include "modules.h"\r
+\r
+\r
+TREATY treaty_registry[] = {\r
+        #define TREATY_REGISTER\r
+        #include "modules.h"\r
+        NULL\r
+        };\r
+\r
+TREATY container_registry[] = {\r
+        #define CONTAINER_REGISTER\r
+        #include "modules.h"\r
+        NULL\r
+\r
+};\r
+\r
diff --git a/babel/register_ifiction.c b/babel/register_ifiction.c
new file mode 100644 (file)
index 0000000..55fe9e2
--- /dev/null
@@ -0,0 +1,29 @@
+/* register_ifiction.c    Register modules for babel's ifiction API\r
+ *\r
+ * 2006 by L. Ross Raszewski\r
+ *\r
+ * This code is freely usable for all purposes.\r
+ *\r
+ * This work is licensed under the Creative Commons Attribution2.5 License.\r
+ * To view a copy of this license, visit\r
+ * http://creativecommons.org/licenses/by/2.5/ or send a letter to\r
+ * Creative Commons,\r
+ * 543 Howard Street, 5th Floor,\r
+ * San Francisco, California, 94105, USA.\r
+ *\r
+ * This file depends on modules.h\r
+ *\r
+ * This version of register.c is stripped down to include only the\r
+ * needed functionality for the ifiction api\r
+ */\r
+\r
+#include <stdlib.h>\r
+#include "treaty.h"\r
+\r
+char *format_registry[] = {\r
+        #define TREATY_REGISTER\r
+        #define CONTAINER_REGISTER\r
+        #define FORMAT_REGISTER\r
+        #include "modules.h"\r
+        NULL\r
+};\r
diff --git a/babel/tads.c b/babel/tads.c
new file mode 100644 (file)
index 0000000..bde1e5a
--- /dev/null
@@ -0,0 +1,1827 @@
+/* \r
+ *   tads.c - Treaty of Babel common functions for tads2 and tads3 modules\r
+ *   \r
+ *   This file depends on treaty_builder.h\r
+ *   \r
+ *   This file is public domain, but note that any changes to this file may\r
+ *   render it noncompliant with the Treaty of Babel\r
+ *   \r
+ *   Modified\r
+ *.   04/08/2006 LRRaszewski - changed babel API calls to threadsafe versions\r
+ *.   04/08/2006 MJRoberts  - initial implementation\r
+ */\r
+\r
+\r
+#include "treaty.h"\r
+#include <ctype.h>\r
+#include <stdio.h>\r
+#include <string.h>\r
+#include <stdlib.h>\r
+#include "tads.h"\r
+#include "md5.h"\r
+\r
+#define ASSERT_OUTPUT_SIZE(x) \\r
+    do { if (output_extent < (x)) return INVALID_USAGE_RV; } while (0)\r
+\r
+#define T2_SIGNATURE "TADS2 bin\012\015\032"\r
+#define T3_SIGNATURE "T3-image\015\012\032"\r
+\r
+#ifndef FALSE\r
+#define FALSE 0\r
+#endif\r
+#ifndef TRUE\r
+#define TRUE 1\r
+#endif\r
+\r
+/* ------------------------------------------------------------------------ */\r
+/*\r
+ *   private structures \r
+ */\r
+\r
+/*\r
+ *   resource information structure - this encapsulates the location and size\r
+ *   of a binary resource object embedded in a story file \r
+ */\r
+typedef struct resinfo resinfo;\r
+struct resinfo\r
+{\r
+    /* pointer and length of the data in the story file buffer */\r
+    const char *ptr;\r
+    int32 len;\r
+\r
+    /* tads major version (currently, 2 or 3) */\r
+    int tads_version;\r
+};\r
+\r
+/*\r
+ *   Name/value pair list entry \r
+ */\r
+typedef struct valinfo valinfo;\r
+struct valinfo\r
+{\r
+    const char *name;\r
+    size_t name_len;\r
+\r
+    /* value string */\r
+    char *val;\r
+    size_t val_len;\r
+\r
+    /* next entry in the list */\r
+    valinfo *nxt;\r
+};\r
+\r
+\r
+/* ------------------------------------------------------------------------ */\r
+/*\r
+ *   forward declarations \r
+ */\r
+static valinfo *parse_game_info(const void *story_file, int32 story_len,\r
+                                int *version);\r
+static int find_resource(const void *story_file, int32 story_len,\r
+                         const char *resname, resinfo *info);\r
+static int find_cover_art(const void *story_file, int32 story_len,\r
+                          resinfo *resp, int32 *image_format,\r
+                          int32 *width, int32 *height);\r
+static int t2_find_res(const void *story_file, int32 story_len,\r
+                       const char *resname, resinfo *info);\r
+static int t3_find_res(const void *story_file, int32 story_len,\r
+                       const char *resname, resinfo *info);\r
+static valinfo *find_by_key(valinfo *list_head, const char *key);\r
+static void delete_valinfo_list(valinfo *head);\r
+static int32 generate_md5_ifid(void *story_file, int32 extent,\r
+                               char *output, int32 output_extent);\r
+static int32 synth_ifiction(valinfo *vals, int tads_version,\r
+                            char *buf, int32 bufsize,\r
+                            void *story_file, int32 extent);\r
+static int get_png_dim(const void *img, int32 extent,\r
+                       int32 *xout, int32 *yout);\r
+static int get_jpeg_dim(const void *img, int32 extent,\r
+                        int32 *xout, int32 *yout);\r
+\r
+\r
+\r
+/* ------------------------------------------------------------------------ */\r
+/*\r
+ *   Get the IFID for a given story file.  \r
+ */\r
+int32 tads_get_story_file_IFID(void *story_file, int32 extent,\r
+                               char *output, int32 output_extent)\r
+{\r
+    valinfo *vals;\r
+    \r
+    /* if we have GameInfo, try looking for an IFID there */\r
+    if ((vals = parse_game_info(story_file, extent, 0)) != 0)\r
+    {\r
+        valinfo *val;\r
+        int found = 0;\r
+        \r
+        /* find the "IFID" key */\r
+        if ((val = find_by_key(vals, "IFID")) != 0)\r
+        {\r
+            char *p;\r
+            \r
+            /* copy the output as a null-terminated string */\r
+            ASSERT_OUTPUT_SIZE((int32)val->val_len + 1);\r
+            memcpy(output, val->val, val->val_len);\r
+            output[val->val_len] = '\0';\r
+\r
+            /* \r
+             *   count up the IFIDs in the buffer - there might be more than\r
+             *   one, separated by commas \r
+             */\r
+            for (found = 1, p = output ; *p != '\0' ; ++p)\r
+            {\r
+                /* if this is a comma, it delimits a new IFID */\r
+                if (*p == ',')\r
+                    ++found;\r
+            }\r
+        }\r
+\r
+        /* delete the GameInfo list */\r
+        delete_valinfo_list(vals);\r
+\r
+        /* if we found an IFID, indicate how many results we found */\r
+        if (found != 0)\r
+            return found;\r
+    }\r
+\r
+    /* \r
+     *   we didn't find an IFID in the GameInfo, so generate a default IFID\r
+     *   using the MD5 method \r
+     */\r
+    return generate_md5_ifid(story_file, extent, output, output_extent);\r
+}\r
+\r
+/*\r
+ *   Get the size of the ifiction metadata for the game \r
+ */\r
+int32 tads_get_story_file_metadata_extent(void *story_file, int32 extent)\r
+{\r
+    valinfo *vals;\r
+    int32 ret;\r
+    int ver;\r
+    \r
+    /*\r
+     *   First, make sure we have a GameInfo record.  If we don't, simply\r
+     *   indicate that there's no metadata to fetch.  \r
+     */\r
+    if ((vals = parse_game_info(story_file, extent, &ver)) == 0)\r
+        return NO_REPLY_RV;\r
+\r
+    /*\r
+     *   Run the ifiction synthesizer with no output buffer, to calculate the\r
+     *   size we need. \r
+     */\r
+    ret = synth_ifiction(vals, ver, 0, 0, story_file, extent);\r
+\r
+    /* delete the value list */\r
+    delete_valinfo_list(vals);\r
+\r
+    /* return the required size */\r
+    return ret;\r
+}\r
+\r
+/*\r
+ *   Get the ifiction metadata for the game\r
+ */\r
+int32 tads_get_story_file_metadata(void *story_file, int32 extent,\r
+                                   char *buf, int32 bufsize)\r
+{\r
+    valinfo *vals;\r
+    int32 ret;\r
+    int ver;\r
+\r
+    /* make sure we have metadata to fetch */\r
+    if ((vals = parse_game_info(story_file, extent, &ver)) == 0)\r
+        return NO_REPLY_RV;\r
+\r
+    /* synthesize the ifiction data into the output buffer */\r
+    ret = synth_ifiction(vals, ver, buf, bufsize, story_file, extent);\r
+\r
+    /* if that required more space than we had available, return an error */\r
+    if (ret > bufsize)\r
+        ret = INVALID_USAGE_RV;\r
+\r
+    /* delete the value list */\r
+    delete_valinfo_list(vals);\r
+\r
+    /* return the result */\r
+    return ret;\r
+}\r
+\r
+/*\r
+ *   Get the size of the cover art \r
+ */\r
+int32 tads_get_story_file_cover_extent(void *story_file, int32 story_len)\r
+{\r
+    resinfo res;\r
+    \r
+    /* look for the cover art resource */\r
+    if (find_cover_art(story_file, story_len, &res, 0, 0, 0))\r
+        return res.len;\r
+    else\r
+        return NO_REPLY_RV;\r
+}\r
+\r
+/*\r
+ *   Get the format of the cover art \r
+ */\r
+int32 tads_get_story_file_cover_format(void *story_file, int32 story_len)\r
+{\r
+    int32 typ;\r
+\r
+    /* look for CoverArt.jpg */\r
+    if (find_cover_art(story_file, story_len, 0, &typ, 0, 0))\r
+        return typ;\r
+    else\r
+        return NO_REPLY_RV;\r
+}\r
+\r
+/*\r
+ *   Get the cover art data \r
+ */\r
+int32 tads_get_story_file_cover(void *story_file, int32 story_len,\r
+                                void *outbuf, int32 output_extent)\r
+{\r
+    resinfo res;\r
+\r
+    /* look for CoverArt.jpg, then for CoverArt.png */\r
+    if (find_cover_art(story_file, story_len, &res, 0, 0, 0))\r
+    {\r
+        /* got it - copy the data to the buffer */\r
+        ASSERT_OUTPUT_SIZE(res.len);\r
+        memcpy(outbuf, res.ptr, res.len);\r
+\r
+        /* success */\r
+        return res.len;\r
+    }\r
+\r
+    /* otherwise, we didn't find it */\r
+    return NO_REPLY_RV;\r
+}\r
+\r
+/* ------------------------------------------------------------------------ */\r
+/*\r
+ *   Generate a default IFID using the MD5 hash method \r
+ */\r
+static int32 generate_md5_ifid(void *story_file, int32 extent,\r
+                               char *output, int32 output_extent)\r
+{\r
+    md5_state_t md5;\r
+    unsigned char md5_buf[16];\r
+    char *p;\r
+    int i;\r
+\r
+    /* calculate the MD5 hash of the story file */\r
+    md5_init(&md5);\r
+    md5_append(&md5, story_file, extent);\r
+    md5_finish(&md5, md5_buf);\r
+\r
+    /* make sure we have room to store the result */\r
+    ASSERT_OUTPUT_SIZE(39);\r
+\r
+    /* the prefix is "TADS2-" or "TADS3-", depending on the format */\r
+    if (tads_match_sig(story_file, extent, T2_SIGNATURE))\r
+        strcpy(output, "TADS2-");\r
+    else\r
+        strcpy(output, "TADS3-");\r
+\r
+    /* the rest is the MD5 hash of the file, as hex digits */\r
+    for (i = 0, p = output + strlen(output) ; i < 16 ; p += 2, ++i)\r
+        sprintf(p, "%02X", md5_buf[i]);\r
+\r
+    /* indicate that we found one result */\r
+    return 1;\r
+}\r
+\r
+/* ------------------------------------------------------------------------ */\r
+/*\r
+ *   Some UTF-8 utility functions and macros.  We use our own rather than the\r
+ *   ctype.h macros because we're parsing UTF-8 text.  \r
+ */\r
+\r
+/* is c a space? */\r
+#define u_isspace(c) ((unsigned char)(c) < 128 && isspace(c))\r
+\r
+/* is c a horizontal space? */\r
+#define u_ishspace(c) (u_isspace(c) && (c) != '\n' && (c) != '\r')\r
+\r
+/* is-newline - matches \n, \r, and \u2028 */\r
+static int u_isnl(const char *p, int32 len)\r
+{\r
+    return (*p == '\n' \r
+            || *p == '\r'\r
+            || (len >= 3\r
+                && *(unsigned char *)p == 0xe2\r
+                && *(unsigned char *)(p+1) == 0x80\r
+                && *(unsigned char *)(p+2) == 0xa8));\r
+}\r
+\r
+/* skip to the next utf-8 character */\r
+static void nextc(const char **p, int32 *len)\r
+{\r
+    /* skip the first byte */\r
+    if (*len != 0)\r
+        ++*p, --*len;\r
+\r
+    /* skip continuation bytes */\r
+    while (*len != 0 && (**p & 0xC0) == 0x80)\r
+        ++*p, --*len;\r
+}\r
+\r
+/* skip to the previous utf-8 character */\r
+static void prevc(const char **p, int32 *len)\r
+{\r
+    /* move back one byte */\r
+    --*p, ++*len;\r
+\r
+    /* keep skipping as long as we're looking at continuation characters */\r
+    while ((**p & 0xC0) == 0x80)\r
+        --*p, ++*len;\r
+}\r
+\r
+/*\r
+ *   Skip a newline sequence.  Skips all common conventions, including \n,\r
+ *   \r, \n\r, \r\n, and \u2028.  \r
+ */\r
+static void skip_newline(const char **p, int32 *rem)\r
+{\r
+    /* make sure we have something to skip */\r
+    if (*rem == 0)\r
+        return;\r
+\r
+    /* check what we have */\r
+    switch (**(const unsigned char **)p)\r
+    {\r
+    case '\n':\r
+        /* skip \n or \n\r */\r
+        nextc(p, rem);\r
+        if (**p == '\r')\r
+            nextc(p, rem);\r
+        break;\r
+\r
+    case '\r':\r
+        /* skip \r or \r\n */\r
+        nextc(p, rem);\r
+        if (**p == '\n')\r
+            nextc(p, rem);\r
+        break;\r
+\r
+    case 0xe2:\r
+        /* \u2028 (unicode line separator) - just skip the one character */\r
+        nextc(p, rem);\r
+        break;\r
+    }\r
+}\r
+\r
+/*\r
+ *   Skip to the next line \r
+ */\r
+static void skip_to_next_line(const char **p, int32 *rem)\r
+{\r
+    /* look for the next newline */\r
+    for ( ; *rem != 0 ; nextc(p, rem))\r
+    {\r
+        /* if this is a newline of some kind, we're at the end of the line */\r
+        if (u_isnl(*p, *rem))\r
+        {\r
+            /* skip the newline, and we're done */\r
+            skip_newline(p, rem);\r
+            break;\r
+        }\r
+    }\r
+}\r
+\r
+\r
+/* ------------------------------------------------------------------------ */\r
+/*\r
+ *   ifiction synthesizer output context \r
+ */\r
+typedef struct synthctx synthctx;\r
+struct synthctx\r
+{\r
+    /* the current output pointer */\r
+    char *buf;\r
+\r
+    /* the number of bytes remaining in the output buffer */\r
+    int32 buf_size;\r
+\r
+    /* \r
+     *   the total number of bytes needed for the output (this might be more\r
+     *   than we've actually written, since we count up the bytes required\r
+     *   even if we need more space than the buffer provides) \r
+     */\r
+    int32 total_size;\r
+\r
+    /* the head of the name/value pair list from the parsed GameInfo */\r
+    valinfo *vals;\r
+};\r
+\r
+/* initialize a synthesizer context */\r
+static void init_synthctx(synthctx *ctx, char *buf, int32 bufsize,\r
+                          valinfo *vals)\r
+{\r
+    /* set up at the beginning of the output buffer */\r
+    ctx->buf = buf;\r
+    ctx->buf_size = bufsize;\r
+\r
+    /* we haven't written anything to the output buffer yet */\r
+    ctx->total_size = 0;\r
+\r
+    /* remember the name/value pair list */\r
+    ctx->vals = vals;\r
+}\r
+\r
+/* \r
+ *   Write out a chunk to a synthesized ifiction record, updating pointers\r
+ *   and counters.  We won't copy past the end of the buffer, but we'll\r
+ *   continue counting the output length needed in any case.  \r
+ */\r
+static void write_ifiction(synthctx *ctx, const char *src, size_t srclen)\r
+{\r
+    int32 copy_len;\r
+\r
+    /* copy as much as we can, up to the remaining buffer size */\r
+    copy_len = srclen;\r
+    if (copy_len > ctx->buf_size)\r
+        copy_len = ctx->buf_size;\r
+\r
+    /* do the copying, if any */\r
+    if (copy_len != 0)\r
+    {\r
+        /* copy the bytes */\r
+        memcpy(ctx->buf, src, (size_t)copy_len);\r
+\r
+        /* adjust the buffer pointer and output buffer size remaining */\r
+        ctx->buf += copy_len;\r
+        ctx->buf_size -= copy_len;\r
+    }\r
+\r
+    /* count this source data in the total size */\r
+    ctx->total_size += srclen;\r
+}\r
+\r
+/* write a null-terminated chunk to the synthesized ifiction record */\r
+static void write_ifiction_z(synthctx *ctx, const char *src)\r
+{\r
+    write_ifiction(ctx, src, strlen(src));\r
+}\r
+\r
+/*\r
+ *   Write a PCDATA string to the synthesized ifiction record.  In\r
+ *   particular, we rewrite '<', '>', and '&' as '&lt;', '&gt;', and '&amp;',\r
+ *   respectively; we trim off leading and trailing spaces; and we compress\r
+ *   each run of whitespace down to a single \u0020 (' ') character.\r
+ */\r
+static void write_ifiction_pcdata(synthctx *ctx, const char *p, size_t len)\r
+{\r
+    /* first, skip any leading whitespace */\r
+    for ( ; len != 0 && u_ishspace(*p) ; ++p, --len) ;\r
+\r
+    /* keep going until we run out of string */\r
+    for (;;)\r
+    {\r
+        const char *start;\r
+        \r
+        /* scan to the next whitespace or markup-significant character */\r
+        for (start = p ;\r
+             len != 0 && !u_ishspace(*p)\r
+             && *p != '<' && *p != '>' && *p != '&' ; ++p, --len) ;\r
+\r
+        /* write the part up to here */\r
+        if (p != start)\r
+            write_ifiction(ctx, start, p - start);\r
+\r
+        /* if we've reached the end of the string, we can stop */\r
+        if (len == 0)\r
+            break;\r
+\r
+        /* check what stopped us */\r
+        switch (*p)\r
+        {\r
+        case '<':\r
+            write_ifiction_z(ctx, "&lt;");\r
+            ++p, --len;\r
+            break;\r
+\r
+        case '>':\r
+            write_ifiction_z(ctx, "&gt;");\r
+            ++p, --len;\r
+            break;\r
+\r
+        case '&':\r
+            write_ifiction_z(ctx, "&amp;");\r
+            ++p, --len;\r
+            break;\r
+\r
+        default:\r
+            /* \r
+             *   The only other thing that could have stopped us is\r
+             *   whitespace.  Skip all consecutive whitespace. \r
+             */\r
+            for ( ; len != 0 && u_ishspace(*p) ; ++p, --len);\r
+\r
+            /* \r
+             *   if that's not the end of the string, replace the run of\r
+             *   whitespace with a single space character in the output; if\r
+             *   we've reached the end of the string, we don't even want to\r
+             *   do that, since we want to trim off trailing spaces \r
+             */\r
+            if (len != 0)\r
+                write_ifiction_z(ctx, " ");\r
+            break;\r
+        }\r
+    }\r
+}\r
+\r
+/*\r
+ *   Translate a GameInfo keyed value to the corresponding ifiction tagged\r
+ *   value.  We find the GameInfo value keyed by 'gameinfo_key', and write\r
+ *   out the same string under the ifiction XML tag 'ifiction_tag'.  We write\r
+ *   a complete XML container sequence - <tag>value</tag>.\r
+ *   \r
+ *   If the given GameInfo key doesn't exist, we use the default value string\r
+ *   'dflt', if given.  If the GameInfo key doesn't exist and 'dflt' is null,\r
+ *   we don't write anything - we don't even write the open/close tags.\r
+ *   \r
+ *   If 'html' is true, we assume the value is in html format, and we write\r
+ *   it untranslated.  Otherwise, we write it as PCDATA, translating markup\r
+ *   characters into '&' entities and compressing whitespace.  \r
+ */\r
+static void write_ifiction_xlat_base(synthctx *ctx, int indent,\r
+                                     const char *gameinfo_key,\r
+                                     const char *ifiction_tag,\r
+                                     const char *dflt, int html)\r
+{\r
+    valinfo *val;\r
+    const char *valstr;\r
+    size_t vallen;\r
+    \r
+    /* look up the GameInfo key */\r
+    if ((val = find_by_key(ctx->vals, gameinfo_key)) != 0)\r
+    {\r
+        /* we found the GameInfo value - use it */\r
+        valstr = val->val;\r
+        vallen = val->val_len;\r
+    }\r
+    else if (dflt != 0)\r
+    {\r
+        /* the GameInfo value doesn't exist, but we have a default - use it */\r
+        valstr = dflt;\r
+        vallen = strlen(dflt);\r
+    }\r
+    else\r
+    {\r
+        /* there's no GameInfo value and no default, so write nothing */\r
+        return;\r
+    }\r
+\r
+    /* write the indentation */\r
+    while (indent != 0)\r
+    {\r
+        static const char spaces[] = "          ";\r
+        size_t cur;\r
+\r
+        /* figure how much we can write on this round */\r
+        cur = indent;\r
+        if (cur > sizeof(spaces) - 1)\r
+            cur = sizeof(spaces) - 1;\r
+\r
+        /* write it */\r
+        write_ifiction(ctx, spaces, cur);\r
+\r
+        /* deduct it from the amount remaining */\r
+        indent -= cur;\r
+    }\r
+\r
+    /* write the open tag */\r
+    write_ifiction_z(ctx, "<");\r
+    write_ifiction_z(ctx, ifiction_tag);\r
+    write_ifiction_z(ctx, ">");\r
+\r
+    /* write the value, applying pcdata translations */\r
+    if (html)\r
+        write_ifiction(ctx, valstr, vallen);\r
+    else\r
+        write_ifiction_pcdata(ctx, valstr, vallen);\r
+\r
+    /* write the close tag */\r
+    write_ifiction_z(ctx, "</");\r
+    write_ifiction_z(ctx, ifiction_tag);\r
+    write_ifiction_z(ctx, ">\n");\r
+}\r
+\r
+#define write_ifiction_xlat(ctx, indent, gikey, iftag, dflt) \\r
+    write_ifiction_xlat_base(ctx, indent, gikey, iftag, dflt, FALSE)\r
+\r
+#define write_ifiction_xlat_html(ctx, indent, gikey, iftag, dflt) \\r
+    write_ifiction_xlat_base(ctx, indent, gikey, iftag, dflt, TRUE)\r
+\r
+\r
+/*\r
+ *   Retrieve the next author name from the GameInfo "Author" format.  The\r
+ *   format is as follows:\r
+ *   \r
+ *   name <email> <email>... ; ...\r
+ *   \r
+ *   That is, each author is listed with a name followed by one or more email\r
+ *   addresses in angle brackets, and multiple authors are separated by\r
+ *   semicolons.  \r
+ */\r
+static int scan_author_name(const char **p, size_t *len,\r
+                            const char **start, const char **end)\r
+{\r
+    /* keep going until we find a non-empty author name */\r
+    for (;;)\r
+    {\r
+        /* skip leading spaces */\r
+        for ( ; *len != 0 && u_ishspace(**p) ; ++*p, --*len) ;\r
+\r
+        /* if we ran out of string, there's definitely no author name */\r
+        if (*len == 0)\r
+            return FALSE;\r
+\r
+        /* \r
+         *   Find the end of this author name.  The author name ends at the\r
+         *   next semicolon or angle bracket.  \r
+         */\r
+        for (*start = *p ; *len != 0 && **p != ';' && **p != '<' ;\r
+             ++*p, --*len) ;\r
+\r
+        /* trim off any trailing spaces */\r
+        for (*end = *p ; *end > *start && u_ishspace(*(*end - 1)) ; --*end) ;\r
+\r
+        /* now skip any email addresses */\r
+        while (*len != 0 && **p == '<')\r
+        {\r
+            /* skip to the closing bracket */\r
+            for (++*p, --*len ; *len != 0 && **p != '>' ; ++*p, --*len) ;\r
+\r
+            /* skip the bracket */\r
+            if (*len != 0)\r
+                ++*p, --*len;\r
+\r
+            /* skip whitespace */\r
+            for ( ; *len != 0 && u_ishspace(**p) ; ++*p, --*len) ;\r
+\r
+            /* \r
+             *   if we're not at a semicolon, another angle bracket, or the\r
+             *   end of the string, it's a syntax error \r
+             */\r
+            if (*len != 0 && **p != '<' && **p != ';')\r
+            {\r
+                *len = 0;\r
+                return FALSE;\r
+            }\r
+        }\r
+\r
+        /* if we're at a semicolon, skip it */\r
+        if (*len != 0 && **p == ';')\r
+            ++*p, --*len;\r
+\r
+        /* \r
+         *   if we found a non-empty name, return it; otherwise, continue on\r
+         *   to the next semicolon section \r
+         */\r
+        if (*end != *start)\r
+            return TRUE;\r
+    }\r
+}\r
+\r
+\r
+/*\r
+ *   Synthesize an ifiction record for the given GameInfo name/value pair\r
+ *   list.  Returns the number of bytes required for the result, including\r
+ *   null termination.  We'll copy as much as we can to the output buffer, up\r
+ *   to bufsize; if the buffer size is insufficient to hold the result, we'll\r
+ *   still indicate the length needed for the full result, but we're careful\r
+ *   not to actually copy anything past the end of the buffer.  \r
+ */\r
+static int32 synth_ifiction(valinfo *vals, int tads_version,\r
+                            char *buf, int32 bufsize,\r
+                            void *story_file, int32 extent)\r
+{\r
+    char default_ifid[TREATY_MINIMUM_EXTENT];\r
+    valinfo *ifid = find_by_key(vals, "IFID");\r
+    const char *ifid_val;\r
+    size_t ifid_len;\r
+    valinfo *author = find_by_key(vals, "AuthorEmail");\r
+    valinfo *url = find_by_key(vals, "Url");\r
+    synthctx ctx;\r
+    const char *p;\r
+    size_t rem;\r
+    int32 art_fmt;\r
+    int32 art_wid, art_ht;\r
+\r
+    /* initialize the output content */\r
+    init_synthctx(&ctx, buf, bufsize, vals);\r
+\r
+    /* make sure the tads version is one we know how to handle */\r
+    if (tads_version != 2 && tads_version != 3)\r
+        return NO_REPLY_RV;\r
+\r
+    /* \r
+     *   The IFID is mandatory.  If there's not an IFID specifically listed\r
+     *   in the GameInfo, we need to generate the default IFID based on the\r
+     *   MD5 hash of the game file. \r
+     */\r
+    if (ifid != 0)\r
+    {\r
+        /* use the explicit IFID(s) listed in the GameInfo */\r
+        ifid_val = ifid->val;\r
+        ifid_len = ifid->val_len;\r
+    }\r
+    else\r
+    {\r
+        /* generate the default IFID */\r
+        generate_md5_ifid(story_file, extent,\r
+                          default_ifid, TREATY_MINIMUM_EXTENT);\r
+\r
+        /* use this as the IFID */\r
+        ifid_val = default_ifid;\r
+        ifid_len = strlen(default_ifid);\r
+    }\r
+\r
+    /* write the header, and start the <identification> section */\r
+    write_ifiction_z(\r
+        &ctx,\r
+        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"\r
+        "<ifindex version=\"1.0\" "\r
+        "xmlns=\"http://babel.ifarchive.org/protocol/iFiction/\">\n"\r
+        "  <!-- Bibliographic data translated from TADS GameInfo -->\n"\r
+        "  <story>\n"\r
+        "    <colophon>\n"\r
+        "     <generator>Babel</generator>\n"\r
+        "     <generatorversion>" TREATY_VERSION "</generatorversion>\n"\r
+        "      <originated>2006-04-14</originated>\n"\r
+        "     </colophon>\n"\r
+        "    <identification>\n");\r
+\r
+    /* write each IFID (there might be several) */\r
+    for (p = ifid_val, rem = ifid_len ; rem != 0 ; )\r
+    {\r
+        const char *start;\r
+        const char *end;\r
+\r
+        /* skip leading spaces */\r
+        for ( ; rem != 0 && u_ishspace(*p) ; ++p, --rem) ;\r
+        \r
+        /* find the end of this IFID */\r
+        for (start = p ; rem != 0 && *p != ',' ; ++p, --rem) ;\r
+\r
+        /* remove trailing spaces */\r
+        for (end = p ; end > start && u_ishspace(*(end-1)) ; --end) ;\r
+\r
+        /* if we found one, write it out */\r
+        if (end != start)\r
+        {\r
+            write_ifiction_z(&ctx, "      <ifid>");\r
+            write_ifiction(&ctx, start, end - start);\r
+            write_ifiction_z(&ctx, "</ifid>\n");\r
+        }\r
+\r
+        /* skip the comma */\r
+        if (rem != 0 && *p == ',')\r
+            ++p, --rem;\r
+    }\r
+\r
+    /* add the format information */\r
+    write_ifiction_z(&ctx,\r
+                     tads_version == 2\r
+                     ? "      <format>tads2</format>\n"\r
+                     : "      <format>tads3</format>\n");\r
+\r
+    /* close the <identification> section and start the <bibliographic> */\r
+    write_ifiction_z(&ctx,\r
+                     "    </identification>\n"\r
+                     "    <bibliographic>\n");\r
+\r
+    /* write the various bibliographic data */\r
+    write_ifiction_xlat(&ctx, 6, "Name", "title", "An Interactive Fiction");\r
+    write_ifiction_xlat(&ctx, 6, "Headline", "headline", 0);\r
+    write_ifiction_xlat(&ctx, 6, "Desc", "description", 0);\r
+    write_ifiction_xlat(&ctx, 6, "Genre", "genre", 0);\r
+    write_ifiction_xlat(&ctx, 6, "Forgiveness", "forgiveness", 0);\r
+    write_ifiction_xlat(&ctx, 6, "Series", "series", 0);\r
+    write_ifiction_xlat(&ctx, 6, "SeriesNumber", "seriesnumber", 0);\r
+    write_ifiction_xlat(&ctx, 6, "Language", "language", 0);\r
+    write_ifiction_xlat(&ctx, 6, "FirstPublished", "firstpublished", 0);\r
+\r
+    /* if there's an author, write the list of author names */\r
+    if (author != 0)\r
+    {\r
+        int cnt;\r
+        int i;\r
+        const char *start;\r
+        const char *end;\r
+\r
+        /* start the <author> tag */\r
+        write_ifiction_z(&ctx, "      <author>");\r
+        \r
+        /* \r
+         *   first, count up the number of authors - authors are separated by\r
+         *   semicolons, so there's one more author than there are semicolons\r
+         */\r
+        for (p = author->val, rem = author->val_len, cnt = 1 ;\r
+             scan_author_name(&p, &rem, &start, &end) ; ) ;\r
+\r
+        /* \r
+         *   Now generate the list of authors.  If there are multiple\r
+         *   authors, use commas to separate them. \r
+         */\r
+        for (p = author->val, rem = author->val_len, i = 0 ; ; ++i)\r
+        {\r
+            /* scan this author's name */\r
+            if (!scan_author_name(&p, &rem, &start, &end))\r
+                break;\r
+            \r
+            /* write out this author name */\r
+            write_ifiction_pcdata(&ctx, start, end - start);\r
+\r
+            /* if there's another name to come, write a separator */\r
+            if (i + 1 < cnt)\r
+            {\r
+                /* \r
+                 *   write just "and" to separate two items; write ","\r
+                 *   between items in lists of more than two, with ",and"\r
+                 *   between the last two items \r
+                 */\r
+                write_ifiction_z(&ctx,\r
+                                 cnt == 2 ? " and " :\r
+                                 i + 2 < cnt ? ", " : ", and ");\r
+            }\r
+        }\r
+\r
+        /* end the <author> tag */\r
+        write_ifiction_z(&ctx, "</author>\n");\r
+    }\r
+\r
+    /* end the biblio section */\r
+    write_ifiction_z(&ctx, "    </bibliographic>\n");\r
+\r
+    /* if there's cover art, add its information */\r
+    if (find_cover_art(story_file, extent, 0, &art_fmt, &art_wid, &art_ht)\r
+        && (art_fmt == PNG_COVER_FORMAT || art_fmt == JPEG_COVER_FORMAT))\r
+    {\r
+        char buf[200];\r
+        \r
+        sprintf(buf,\r
+                "    <cover>\n"\r
+                "        <format>%s</format>\n"\r
+                "        <height>%lu</height>\n"\r
+                "        <width>%lu</width>\n"\r
+                "    </cover>\n",\r
+                art_fmt == PNG_COVER_FORMAT ? "png" : "jpg",\r
+                (long)art_ht, (long)art_wid);\r
+\r
+        write_ifiction_z(&ctx, buf);\r
+    }\r
+\r
+    /* if there's an author email, include it */\r
+    if (author != 0 || url != 0)\r
+    {\r
+        const char *p;\r
+        size_t rem;\r
+        int i;\r
+        \r
+        /* open the section */\r
+        write_ifiction_z(&ctx, "    <contacts>\n");\r
+\r
+        /* add the author email, if provided */\r
+        if (author != 0)\r
+        {\r
+            /* write the email list */\r
+            for (i = 0, p = author->val, rem = author->val_len ; ; ++i)\r
+            {\r
+                const char *start;\r
+                \r
+                /* skip to the next email address */\r
+                for ( ; rem != 0 && *p != '<' ; ++p, --rem) ;\r
+                \r
+                /* if we didn't find an email address, we're done */\r
+                if (rem == 0)\r
+                    break;\r
+                \r
+                /* find the matching '>' */\r
+                for (++p, --rem, start = p ; rem != 0 && *p != '>' ;\r
+                     ++p, --rem) ;\r
+\r
+                /* \r
+                 *   if this is the first one, open the section; otherwise,\r
+                 *   add a comma \r
+                 */\r
+                if (i == 0)\r
+                    write_ifiction_z(&ctx, "      <authoremail>");\r
+                else\r
+                    write_ifiction_z(&ctx, ",");\r
+                \r
+                /* write this address */\r
+                write_ifiction(&ctx, start, p - start);\r
+                \r
+                /* \r
+                 *   skip the closing bracket, if there is one; if we're out\r
+                 *   of string, we're done \r
+                 */\r
+                if (rem != 0)\r
+                    ++p, --rem;\r
+                else\r
+                    break;\r
+            }\r
+\r
+            /* if we found any emails to write, end the section */\r
+            if (i != 0)\r
+                write_ifiction_z(&ctx, "</authoremail>\n");\r
+        }\r
+\r
+        /* if there's a URL, add it */\r
+        if (url != 0)\r
+        {\r
+            write_ifiction_z(&ctx, "      <url>");\r
+            write_ifiction(&ctx, url->val, url->val_len);\r
+            write_ifiction_z(&ctx, "</url>\n");\r
+        }\r
+\r
+        /* close the section */\r
+        write_ifiction_z(&ctx, "    </contacts>\n");\r
+    }\r
+\r
+    /* add the tads-specific section */\r
+    write_ifiction_z(&ctx, "    <tads>\n");\r
+    \r
+    write_ifiction_xlat(&ctx, 6, "Version", "version", 0);\r
+    write_ifiction_xlat(&ctx, 6, "ReleaseDate", "releasedate", 0);\r
+    write_ifiction_xlat(&ctx, 6, "PresentationProfile",\r
+                        "presentationprofile", 0);\r
+    write_ifiction_xlat(&ctx, 6, "Byline", "byline", 0);\r
+\r
+    write_ifiction_z(&ctx, "    </tads>\n");\r
+\r
+    /* close the story section and the main body */\r
+    write_ifiction_z(&ctx, "  </story>\n</ifindex>\n");\r
+    \r
+    /* add the null terminator */\r
+    write_ifiction(&ctx, "", 1);\r
+\r
+    /* return the total output size */\r
+    return ctx.total_size;\r
+}\r
+\r
+/* ------------------------------------------------------------------------ */\r
+/*\r
+ *   Check a data block to see if it starts with the given signature. \r
+ */\r
+int tads_match_sig(const void *buf, int32 len, const char *sig)\r
+{\r
+    /* note the length of the signature string */\r
+    size_t sig_len = strlen(sig);\r
+    \r
+    /* if matches if the buffer starts with the signature string */\r
+    return (len >= (int32)sig_len && memcmp(buf, sig, sig_len) == 0);\r
+}\r
+\r
+\r
+/* ------------------------------------------------------------------------ */\r
+/*\r
+ *   portable-to-native format conversions \r
+ */\r
+#define osbyte(p, ofs) \\r
+    (*(((unsigned char *)(p)) + (ofs)))\r
+\r
+#define osrp1(p) \\r
+    ((unsigned int)osbyte(p, 0))\r
+\r
+#define osrp2(p) \\r
+    ((unsigned int)osbyte(p, 0) \\r
+    + ((unsigned int)osbyte(p, 1) << 8))\r
+\r
+#define osrp4(p) \\r
+    (((unsigned long)osbyte(p, 0)) \\r
+    + (((unsigned long)osbyte(p, 1)) << 8) \\r
+    + (((unsigned long)osbyte(p, 2)) << 16) \\r
+    + (((unsigned long)osbyte(p, 3)) << 24))\r
+\r
+\r
+/* ------------------------------------------------------------------------ */\r
+/*\r
+ *   Parse a game file and retrieve the GameInfo data.  Returns the head of a\r
+ *   linked list of valinfo entries.\r
+ */\r
+static valinfo *parse_game_info(const void *story_file, int32 story_len,\r
+                                int *tads_version)\r
+{\r
+    resinfo res;\r
+    const char *p;\r
+    int32 rem;\r
+    valinfo *val_head = 0;\r
+\r
+    /* \r
+     *   first, find the GameInfo resource - if it's not there, there's no\r
+     *   game information to parse \r
+     */\r
+    if (!find_resource(story_file, story_len, "GameInfo.txt", &res))\r
+        return 0;\r
+\r
+    /* if the caller wants the TADS version number, hand it back */\r
+    if (tads_version != 0)\r
+        *tads_version = res.tads_version;\r
+\r
+    /* parse the data */\r
+    for (p = res.ptr, rem = res.len ; rem != 0 ; )\r
+    {\r
+        const char *name_start;\r
+        size_t name_len;\r
+        const char *val_start;\r
+        valinfo *val;\r
+        const char *inp;\r
+        int32 inlen;\r
+        char *outp;\r
+\r
+        /* skip any leading whitespace */\r
+        while (rem != 0 && u_isspace(*p))\r
+            ++p, --rem;\r
+\r
+        /* if the line starts with '#', it's a comment, so skip it */\r
+        if (rem != 0 && *p == '#')\r
+        {\r
+            skip_to_next_line(&p, &rem);\r
+            continue;\r
+        }\r
+\r
+        /* we must have the start of a name - note it */\r
+        name_start = p;\r
+\r
+        /* skip ahead to a space or colon */\r
+        while (rem != 0 && *p != ':' && !u_ishspace(*p))\r
+            nextc(&p, &rem);\r
+\r
+        /* note the length of the name */\r
+        name_len = p - name_start;\r
+\r
+        /* skip any whitespace before the presumed colon */\r
+        while (rem != 0 && u_ishspace(*p))\r
+            nextc(&p, &rem);\r
+\r
+        /* if we're not at a colon, the line is ill-formed, so skip it */\r
+        if (rem == 0 || *p != ':')\r
+        {\r
+            /* skip the entire line, and go back for the next one */\r
+            skip_to_next_line(&p, &rem);\r
+            continue;\r
+        }\r
+\r
+        /* skip the colon and any whitespace immediately after it */\r
+        for (nextc(&p, &rem) ; rem != 0 && u_ishspace(*p) ; nextc(&p, &rem)) ;\r
+\r
+        /* note where the value starts */\r
+        val_start = p;\r
+\r
+        /*\r
+         *   Scan the value to get its length.  The value runs from here to\r
+         *   the next newline that's not followed immediately by a space. \r
+         */\r
+        while (rem != 0)\r
+        {\r
+            const char *nl;\r
+            int32 nlrem;\r
+            \r
+            /* skip to the next line */\r
+            skip_to_next_line(&p, &rem);\r
+\r
+            /* if we're at eof, we can stop now */\r
+            if (rem == 0)\r
+                break;\r
+\r
+            /* note where this line starts */\r
+            nl = p;\r
+            nlrem = rem;\r
+\r
+            /* \r
+             *   if we're at a non-whitespace character, it's definitely not\r
+             *   a continuation line \r
+             */\r
+            if (!u_ishspace(*p))\r
+                break;\r
+\r
+            /* \r
+             *   check for spaces followed by a non-space character - this\r
+             *   would signify a continuation line\r
+             */\r
+            for ( ; rem != 0 && u_ishspace(*p) ; nextc(&p, &rem)) ;\r
+            if (rem == 0 || u_isnl(p, rem))\r
+            {\r
+                /* \r
+                 *   we're at end of file, we found a line with nothing but\r
+                 *   whitespace, so this isn't a continuation line; go back\r
+                 *   to the start of this line and end the value here \r
+                 */\r
+                p = nl;\r
+                rem = nlrem;\r
+                break;\r
+            }\r
+\r
+            /* \r
+             *   We found whitespace followed by non-whitespace, so this is a\r
+             *   continuation line.  Keep going for now.\r
+             */\r
+        }\r
+\r
+        /* remove any trailing newlines */\r
+        while (p > val_start)\r
+        {\r
+            /* move back one character */\r
+            prevc(&p, &rem);\r
+\r
+            /* \r
+             *   if it's a newline, keep going; otherwise, keep this\r
+             *   character and stop trimming \r
+             */\r
+            if (!u_isnl(p, rem))\r
+            {\r
+                nextc(&p, &rem);\r
+                break;\r
+            }\r
+        }\r
+\r
+        /* \r
+         *   Allocate a new value entry.  Make room for the entry itself plus\r
+         *   a copy of the value.  We don't need to make a copy of the name,\r
+         *   since we can just use the original copy from the story file\r
+         *   buffer.  We do need a copy of the value because we might need to\r
+         *   transform it slightly, to remove newlines and leading spaces on\r
+         *   continuation lines. \r
+         */\r
+        val = (valinfo *)malloc(sizeof(valinfo) + (p - val_start));\r
+\r
+        /* link it into our list */\r
+        val->nxt = val_head;\r
+        val_head = val;\r
+\r
+        /* point the name directly to the name in the buffer */\r
+        val->name = name_start;\r
+        val->name_len = name_len;\r
+\r
+        /* point the value to the space allocated along with the valinfo */\r
+        val->val = (char *)(val + 1);\r
+\r
+        /* store the name, removing newlines and continuation-line spaces */\r
+        for (outp = val->val, inp = val_start, inlen = p - val_start ;\r
+             inlen != 0 ; )\r
+        {\r
+            const char *l;\r
+            \r
+            /* find the next newline */\r
+            for (l = inp ; inlen != 0 && !u_isnl(inp, inlen) ;\r
+                 nextc(&inp, &inlen)) ;\r
+\r
+            /* copy this line to the output */\r
+            memcpy(outp, l, inp - l);\r
+            outp += inp - l;\r
+\r
+            /* if we're out of input, we're done */\r
+            if (inlen == 0)\r
+                break;\r
+\r
+            /* we're at a newline: replace it with a space in the output */\r
+            *outp++ = ' ';\r
+\r
+            /* skip the newline and subsequent whitespace in the input */\r
+            for (skip_newline(&inp, &inlen) ;\r
+                 inlen != 0 && u_ishspace(*inp) ; nextc(&inp, &inlen)) ;\r
+        }\r
+\r
+        /* set the length of the parsed value */\r
+        val->val_len = outp - val->val;\r
+\r
+        /* skip to the next line and continue parsing */\r
+        skip_to_next_line(&p, &rem);\r
+    }\r
+\r
+    /* return the head of the linked list of value entries */\r
+    return val_head;\r
+}\r
+static int my_memicmp(const void *aa, const void *bb, int l)\r
+{\r
+ int s=0,i;\r
+ char *a=(char *) aa;\r
+ char *b=(char *) bb;\r
+ for(i=0;i<l && !s;i++)\r
+  s=tolower(a[i])-tolower(b[i]);\r
+ return s;\r
+}\r
+\r
+/* ------------------------------------------------------------------------ */\r
+/*\r
+ *   Given a valinfo list obtained from parse_game_info(), find the value for\r
+ *   the given key \r
+ */\r
+static valinfo *find_by_key(valinfo *list_head, const char *key)\r
+{\r
+    valinfo *p;\r
+    size_t key_len = strlen(key);\r
+    \r
+    /* scan the list for the given key */\r
+    for (p = list_head ; p != 0 ; p = p->nxt)\r
+    {\r
+        /* if this one matches the key we're looking for, return it */\r
+        if (p->name_len == key_len && my_memicmp(p->name, key, key_len) == 0)\r
+            return p;\r
+    }\r
+\r
+    /* no luck */\r
+    return 0;\r
+}\r
+\r
+/* ------------------------------------------------------------------------ */\r
+/*\r
+ *   Delete a valinfo list obtained from parse_game_info() \r
+ */\r
+static void delete_valinfo_list(valinfo *head)\r
+{\r
+    /* keep going until we run out of entries */\r
+    while (head != 0)\r
+    {\r
+        /* remember the next entry, before we delete this one */\r
+        valinfo *nxt = head->nxt;\r
+\r
+        /* delete this one */\r
+        free(head);\r
+\r
+        /* move on to the next one */\r
+        head = nxt;\r
+    }\r
+}\r
+\r
+/* ------------------------------------------------------------------------ */\r
+/*\r
+ *   Find the cover art resource.  We'll look for CoverArt.jpg and\r
+ *   CoverArt.png, in that order. \r
+ */\r
+static int find_cover_art(const void *story_file, int32 story_len,\r
+                          resinfo *resp, int32 *image_format,\r
+                          int32 *width, int32 *height)\r
+{\r
+    resinfo res;\r
+    int32 x, y;\r
+\r
+    /* if they didn't want the resource info, provide a placeholder */\r
+    if (resp == 0)\r
+        resp = &res;\r
+\r
+    /* look for CoverArt.jpg first */\r
+    if (find_resource(story_file, story_len, "CoverArt.jpg", resp))\r
+    {\r
+        /* get the width and height */\r
+        if (!get_jpeg_dim(resp->ptr, resp->len, &x, &y))\r
+            return FALSE;\r
+\r
+        /* hand back the width and height if it was requested */\r
+        if (width != 0)\r
+            *width = x;\r
+        if (height != 0)\r
+            *height = y;\r
+\r
+        /* tell them it's a JPEG image */\r
+        if (image_format != 0)\r
+            *image_format = JPEG_COVER_FORMAT;\r
+\r
+        /* indicate success */\r
+        return TRUE;\r
+    }\r
+\r
+    /* look for CoverArt.png second */\r
+    if (find_resource(story_file, story_len, "CoverArt.png", resp))\r
+    {\r
+        /* get the width and height */\r
+        if (!get_png_dim(resp->ptr, resp->len, &x, &y))\r
+            return FALSE;\r
+\r
+        /* hand back the width and height if it was requested */\r
+        if (width != 0)\r
+            *width = x;\r
+        if (height != 0)\r
+            *height = y;\r
+\r
+        /* tell them it's a PNG image */\r
+        if (image_format != 0)\r
+            *image_format = PNG_COVER_FORMAT;\r
+\r
+        /* indicate success */\r
+        return TRUE;\r
+    }\r
+\r
+    /* didn't find it */\r
+    return FALSE;\r
+}\r
+\r
+/* ------------------------------------------------------------------------ */\r
+/*\r
+ *   Find a resource in a TADS 2 or 3 story file that's been loaded into\r
+ *   memory.  On success, fills in the offset and size of the resource and\r
+ *   returns TRUE; if the resource isn't found, returns FALSE.\r
+ */\r
+static int find_resource(const void *story_file, int32 story_len,\r
+                         const char *resname, resinfo *info)\r
+{\r
+    /* if there's no file, there's no resource */\r
+    if (story_file == 0)\r
+        return FALSE;\r
+\r
+    /* check for tads 2 */\r
+    if (tads_match_sig(story_file, story_len, T2_SIGNATURE))\r
+    {\r
+        info->tads_version = 2;\r
+        return t2_find_res(story_file, story_len, resname, info);\r
+    }\r
+\r
+    /* check for tads 3 */\r
+    if (tads_match_sig(story_file, story_len, T3_SIGNATURE))\r
+    {\r
+        info->tads_version = 3;\r
+        return t3_find_res(story_file, story_len, resname, info);\r
+    }\r
+\r
+    /* it's not one of ours */\r
+    return FALSE;\r
+}\r
+\r
+/* ------------------------------------------------------------------------ */\r
+/*\r
+ *   Find a resource in a tads 2 game file \r
+ */\r
+static int t2_find_res(const void *story_file, int32 story_len,\r
+                       const char *resname, resinfo *info)\r
+{\r
+    const char *basep = (const char *)story_file;\r
+    const char *endp = basep + story_len;\r
+    const char *p;\r
+    size_t resname_len;\r
+\r
+    /* note the length of the name we're seeking */\r
+    resname_len = strlen(resname);\r
+\r
+    /* \r
+     *   skip past the tads 2 file header (13 bytes for the signature, 7\r
+     *   bytes for the version header, 2 bytes for the flags, 26 bytes for\r
+     *   the timestamp) \r
+     */\r
+    p = basep + 13 + 7 + 2 + 26;\r
+\r
+    /* \r
+     *   scan the sections in the file; stop on $EOF, and skip everything\r
+     *   else but HTMLRES, which is the section type that \r
+     */\r
+    while (p < endp)\r
+    {\r
+        unsigned long endofs;\r
+\r
+        /*\r
+         *   We're pointing to a section block header, which looks like this:\r
+         *   \r
+         *.    <byte> type-length\r
+         *.    <byte * type-length> type-name\r
+         *.    <uint32> next-section-address\r
+         */\r
+\r
+        /* read the ending offset */\r
+        endofs = osrp4(p + 1 + osrp1(p));\r
+\r
+        /* check the type */\r
+        if (p[0] == 7 && memcmp(p + 1, "HTMLRES", 7) == 0)\r
+        {\r
+            unsigned long found_ofs;\r
+            int found;\r
+            unsigned long entry_cnt;\r
+\r
+            /* we haven't found the resource yet */\r
+            found = FALSE;\r
+\r
+            /* \r
+             *   It's a multimedia resource block.  Skip the section block\r
+             *   header and look at the index table - the index table\r
+             *   consists of a uint32 giving the number of entries, followed\r
+             *   by a reserved uint32, followed by the entries.  \r
+             */\r
+            p += 12;\r
+            entry_cnt = osrp4(p);\r
+\r
+            /* skip to the first index entry */\r
+            p += 8;\r
+\r
+            /* scan the index entries */\r
+            for ( ; entry_cnt != 0 ; --entry_cnt)\r
+            {\r
+                unsigned long res_ofs;\r
+                unsigned long res_siz;\r
+                size_t name_len;\r
+\r
+                /*\r
+                 *   We're at the next index entry, which looks like this:\r
+                 *\r
+                 *.    <uint32>  resource-address (bytes from end of index)\r
+                 *.    <uint32>  resource-length (in bytes)\r
+                 *.    <uint2> name-length\r
+                 *.    <byte * name-length> name\r
+                 */\r
+                res_ofs = osrp4(p);\r
+                res_siz = osrp4(p + 4);\r
+                name_len = osrp2(p + 8);\r
+                p += 10;\r
+\r
+                /* check for a match to the name we're looking for */\r
+                if (name_len == resname_len\r
+                    && my_memicmp(resname, p, name_len) == 0)\r
+                {\r
+                    /* \r
+                     *   it's the one we want - note its resource location\r
+                     *   and size, but keep scanning for now, since we need\r
+                     *   to find the end of the index before we'll know where\r
+                     *   the actual resources begin \r
+                     */\r
+                    found = TRUE;\r
+                    found_ofs = res_ofs;\r
+                    info->len = res_siz;\r
+                }\r
+\r
+                /* skip this one's name */\r
+                p += name_len;\r
+            }\r
+\r
+            /* \r
+             *   if we found our resource, the current seek position is the\r
+             *   base of the offset we found in the directory; so we can\r
+             *   finally fix up the offset to give the actual file location\r
+             *   and return the result \r
+             */\r
+            if (found)\r
+            {\r
+                /* fix up the offset with the actual file location */\r
+                info->ptr = p + found_ofs;\r
+\r
+                /* tell the caller we found it */\r
+                return TRUE;\r
+            }\r
+        }\r
+        else if (p[0] == 4 && memcmp(p + 1, "$EOF", 4) == 0)\r
+        {\r
+            /* \r
+             *   that's the end of the file - we've finished without finding\r
+             *   the resource, so return failure \r
+             */\r
+            return FALSE;\r
+        }\r
+\r
+        /* move to the next section */\r
+        p = basep + endofs;\r
+    }\r
+\r
+    /* \r
+     *   reached EOF without an $EOF marker - file must be corrupted; return\r
+     *   'not found' \r
+     */\r
+    return FALSE;\r
+}\r
+\r
+/* ------------------------------------------------------------------------ */\r
+/*\r
+ *   Find a resource in a T3 image file \r
+ */\r
+static int t3_find_res(const void *story_file, int32 story_len,\r
+                       const char *resname, resinfo *info)\r
+{\r
+    const char *basep = (const char *)story_file;\r
+    const char *endp = basep + story_len;\r
+    const char *p;\r
+    size_t resname_len;\r
+\r
+    /* note the length of the name we're seeking */\r
+    resname_len = strlen(resname);\r
+\r
+    /* \r
+     *   skip the file header - 11 bytes for the signature, 2 bytes for the\r
+     *   format version, 32 reserved bytes, and 24 bytes for the timestamp \r
+     */\r
+    p = basep + 11 + 2 + 32 + 24;\r
+\r
+    /* scan the data blocks */\r
+    while (p < endp)\r
+    {\r
+        unsigned long siz;\r
+\r
+        /*\r
+         *   We're at the next block header, which looks like this:\r
+         *\r
+         *.    <byte * 4> type-name\r
+         *.    <uint32> block-size\r
+         *.    <uint16> flags\r
+         */\r
+\r
+        /* get the block size */\r
+        siz = osrp4(p + 4);\r
+\r
+        /* check the type */\r
+        if (memcmp(p, "MRES", 4) == 0)\r
+        {\r
+            unsigned int entry_cnt;\r
+            unsigned int i;\r
+            const char *blockp;\r
+\r
+            /* skip the header */\r
+            p += 10;\r
+\r
+            /* \r
+             *   remember the location of the base of the block - the data\r
+             *   seek location for each index entry is given as an offset\r
+             *   from this location \r
+             */\r
+            blockp = p;\r
+\r
+            /* the first thing in the table is the number of entries */\r
+            entry_cnt = osrp2(p);\r
+            p += 2;\r
+\r
+            /* read the entries */\r
+            for (i = 0 ; i < entry_cnt ; ++i)\r
+            {\r
+                unsigned long entry_ofs;\r
+                unsigned long entry_siz;\r
+                size_t entry_name_len;\r
+                char namebuf[256];\r
+                char *xp;\r
+                size_t xi;\r
+\r
+                /* \r
+                 *   Parse this index entry:\r
+                 *   \r
+                 *.    <uint32> address (as offset from the block base)\r
+                 *.    <uint32> size (in bytes)\r
+                 *.    <uint8> name-length\r
+                 *.    <byte * name-length> name (all bytes XORed with 0xFF)\r
+                 */\r
+                entry_ofs = osrp4(p);\r
+                entry_siz = osrp4(p + 4);\r
+                entry_name_len = (unsigned char)p[8];\r
+\r
+                /* unmask the name */\r
+                memcpy(namebuf, p + 9, resname_len);\r
+                for (xi = resname_len, xp = namebuf ; xi != 0 ; --xi)\r
+                    *xp++ ^= 0xFF;\r
+\r
+                /* if this is the one we're looking for, return it */\r
+                if (entry_name_len == resname_len\r
+                    && my_memicmp(resname, namebuf, resname_len) == 0)\r
+                {\r
+                    /* \r
+                     *   fill in the return information - note that the entry\r
+                     *   offset given in the header is an offset from data\r
+                     *   block's starting location, so fix this up to an\r
+                     *   absolute seek location for the return value \r
+                     */\r
+                    info->ptr = blockp + entry_ofs;\r
+                    info->len = entry_siz;\r
+\r
+                    /* return success */\r
+                    return TRUE;\r
+                }\r
+\r
+                /* skip this entry (header + name length) */\r
+                p += 9 + entry_name_len;\r
+            }\r
+\r
+            /* \r
+             *   if we got this far, we didn't find the name; so skip past\r
+             *   the MRES section by adding the section length to the base\r
+             *   pointer, and resume the main file scan \r
+             */\r
+            p = blockp + siz;\r
+        }\r
+        else if (memcmp(p, "EOF ", 4) == 0)\r
+        {\r
+            /* \r
+             *   end of file - we've finished without finding the resource,\r
+             *   so return failure \r
+             */\r
+            return FALSE;\r
+        }\r
+        else\r
+        {\r
+            /* \r
+             *   we don't care about anything else - just skip this block and\r
+             *   keep going; to skip the block, simply seek ahead past the\r
+             *   block header and then past the block's contents, using the\r
+             *   size given the in block header \r
+             */\r
+            p += siz + 10;\r
+        }\r
+    }\r
+\r
+    /* \r
+     *   reached EOF without an EOF marker - file must be corrupted; return\r
+     *   'not found' \r
+     */\r
+    return FALSE;\r
+}\r
+\r
+/* ------------------------------------------------------------------------ */\r
+/*\r
+ *   JPEG and PNG information extraction (based on the versions in\r
+ *   babel_story_functions.c) \r
+ */\r
+static int get_jpeg_dim(const void *img, int32 extent,\r
+                        int32 *xout, int32 *yout)\r
+{\r
+    const unsigned char *dp=(const unsigned char *) img;\r
+    const unsigned char *ep=dp+extent;\r
+    unsigned int t1, t2, w, h;\r
+\r
+    t1 = *dp++;\r
+    t2 = *dp++;\r
+    if (t1 != 0xff || t2 != 0xD8)\r
+        return FALSE;\r
+\r
+    while(1)\r
+    {\r
+        if (dp>ep) return FALSE;\r
+        for(t1=*(dp++);t1!=0xff;t1=*(dp++)) if (dp>ep) return FALSE;\r
+        do { t1=*(dp++); if (dp>ep) return FALSE;} while (t1 == 0xff);\r
+\r
+        if ((t1 & 0xF0) == 0xC0 && !(t1==0xC4 || t1==0xC8 || t1==0xCC))\r
+        {\r
+            dp+=3;\r
+            if (dp>ep) return FALSE;\r
+            h=*(dp++) << 8;\r
+            if (dp>ep) return FALSE;\r
+            h|=*(dp++);\r
+            if (dp>ep) return FALSE;\r
+            w=*(dp++) << 8;\r
+            if (dp>ep) return FALSE;\r
+            w|=*(dp);\r
+\r
+            *xout = w;\r
+            *yout = h;\r
+            return TRUE;\r
+        }\r
+        else if (t1==0xD8 || t1==0xD9)\r
+            break;\r
+        else\r
+        {\r
+            int l;\r
+\r
+            if (dp>ep) return FALSE;\r
+            l=*(dp++) << 8;\r
+            if (dp>ep) return FALSE;\r
+            l|= *(dp++);\r
+            l-=2;\r
+            dp+=l;\r
+            if (dp>ep) return FALSE;\r
+        }\r
+    }\r
+    return FALSE;\r
+}\r
+\r
+static int32 png_read_int(const unsigned char *mem)\r
+{\r
+    int32 i4 = mem[0],\r
+    i3 = mem[1],\r
+    i2 = mem[2],\r
+    i1 = mem[3];\r
+    return i1 | (i2<<8) | (i3<<16) | (i4<<24);\r
+}\r
+\r
+\r
+static int get_png_dim(const void *img, int32 extent,\r
+                       int32 *xout, int32 *yout)\r
+{\r
+    const unsigned char *dp=(const unsigned char *)img;\r
+\r
+    if (extent<33 ||\r
+        !(dp[0]==137 && dp[1]==80 && dp[2]==78 && dp[3]==71 &&\r
+          dp[4]==13 && dp[5] == 10 && dp[6] == 26 && dp[7]==10)||\r
+        !(dp[12]=='I' && dp[13]=='H' && dp[14]=='D' && dp[15]=='R'))\r
+        return FALSE;\r
+\r
+    *xout = png_read_int(dp+16);\r
+    *yout = png_read_int(dp+20);\r
+    return TRUE;\r
+}\r
+\r
+/* ------------------------------------------------------------------------ */\r
+/*\r
+ *   Testing main() - this implements a set of unit tests on the tads\r
+ *   version.  \r
+ */\r
+\r
+#ifdef TADS_TEST\r
+\r
+#include "babel_handler.h"\r
+\r
+void main(int argc, char **argv)\r
+{\r
+    FILE *fp;\r
+    int32 siz;\r
+    void *buf;\r
+    valinfo *head;\r
+    int32 rv;\r
+    int tadsver;\r
+    char outbuf[TREATY_MINIMUM_EXTENT];\r
+\r
+    /* check arguments */\r
+    if (argc != 2)\r
+    {\r
+        printf("usage: tads <game-file>\n");\r
+        exit(1);\r
+    }\r
+\r
+    /* initialize the babel subsystems */\r
+    babel_init(argv[1]);\r
+\r
+    /* open the story file */\r
+    if ((fp = fopen(argv[1], "rb")) == 0)\r
+    {\r
+        printf("error opening input file\n");\r
+        exit(2);\r
+    }\r
+\r
+    /* check the file size */\r
+    fseek(fp, 0, SEEK_END);\r
+    siz = ftell(fp);\r
+    fseek(fp, 0, SEEK_SET);\r
+\r
+    /* allocate space for it */\r
+    if ((buf = malloc(siz)) == 0)\r
+    {\r
+        printf("error allocating space to load file\n");\r
+        exit(2);\r
+    }\r
+\r
+    /* load it */\r
+    if ((int32)fread(buf, 1, siz, fp) != siz)\r
+    {\r
+        printf("error reading file\n");\r
+        exit(2);\r
+    }\r
+\r
+    /* done with the file */\r
+    fclose(fp);\r
+\r
+\r
+\r
+    /* ===== test 1 - basic parse_game_info() test ===== */\r
+\r
+    /* parse the gameinfo record and print the results */\r
+    if ((head = parse_game_info(buf, siz, &tadsver)) != 0)\r
+    {\r
+        valinfo *val;\r
+\r
+        printf("found GameInfo - tads major version = %d\n", tadsver);\r
+        for (val = head ; val != 0 ; val = val->nxt)\r
+        {\r
+            printf("%.*s=[%.*s]\n",\r
+                   (int)val->name_len, val->name,\r
+                   (int)val->val_len, val->val);\r
+        }\r
+        printf("\n");\r
+    }\r
+    else\r
+        printf("no GameInfo found\n\n");\r
+\r
+\r
+\r
+    /* ===== test 2 - test the get_story_file_IFID generator ===== */\r
+    rv = tads_get_story_file_IFID(buf, siz, outbuf, TREATY_MINIMUM_EXTENT);\r
+    if (rv == 1)\r
+        printf("IFID = [%s]\n\n", outbuf);\r
+    else\r
+        printf("IFID return code = %ld\n", rv);\r
+\r
+\r
+\r
+    /* ===== test 3 - test the ifiction synthesizer ===== */\r
+    if ((rv = tads_get_story_file_metadata_extent(buf, siz)) > 0)\r
+    {\r
+        char *ifbuf;\r
+\r
+        /* try allocating the space */\r
+        if ((ifbuf = malloc((size_t)rv)) != 0)\r
+        {\r
+            /* synthesize the story file */\r
+            rv = tads_get_story_file_metadata(buf, siz, ifbuf, rv);\r
+            if (rv > 0)\r
+                printf("ifiction metadata:\n=====\n%.*s\n=====\n\n",\r
+                       (int)rv, ifbuf);\r
+            else\r
+                printf("tads_get_story_file_metadata result = %ld\n", rv);\r
+        }\r
+        else\r
+            printf("unable to allocate %ld bytes for metadata record\n", rv);\r
+    }\r
+    else\r
+        printf("tads_get_story_file_metadata_extent result code = %ld\n", rv);\r
+    \r
+\r
+    /* free the loaded story file buffer */\r
+    free(buf);\r
+}\r
+\r
+\r
+#endif TADS_TEST\r
+\r
diff --git a/babel/tads.h b/babel/tads.h
new file mode 100644 (file)
index 0000000..296b91d
--- /dev/null
@@ -0,0 +1,40 @@
+/*\r
+ *   tads.h - Treaty of Babel common declarations for tads2 and tads3 modules\r
+ *   \r
+ *   This file depends on treaty_builder.h\r
+ *   \r
+ *   This file is public domain, but note that any changes to this file may\r
+ *   render it noncompliant with the Treaty of Babel\r
+ *   \r
+ *   Modified\r
+ *.   04/18/2006 MJRoberts  - creation\r
+ */\r
+\r
+#ifndef TADS_H\r
+#define TADS_H\r
+\r
+/* match a TADS file signature */\r
+int tads_match_sig(const void *buf, int32 len, const char *sig);\r
+\r
+/* get the IFID for a tads story file */\r
+int32 tads_get_story_file_IFID(void *story_file, int32 extent,\r
+                               char *output, int32 output_extent);\r
+\r
+/* get the synthesized iFiction record from a tads story file */\r
+int32 tads_get_story_file_metadata(void *story_file, int32 extent,\r
+                                   char *buf, int32 bufsize);\r
+\r
+/* get the size of the synthesized iFiction record for a tads story file */\r
+int32 tads_get_story_file_metadata_extent(void *story_file, int32 extent);\r
+\r
+/* get the cover art from a tads story file */\r
+int32 tads_get_story_file_cover(void *story_file, int32 extent,\r
+                                void *buf, int32 bufsize);\r
+\r
+/* get the size of the cover art from a tads story file */\r
+int32 tads_get_story_file_cover_extent(void *story_file, int32 extent);\r
+\r
+/* get the image format (jpeg, png) of the covert art in a tads story file */\r
+int32 tads_get_story_file_cover_format(void *story_file, int32 extent);\r
+\r
+#endif /* TADS_H */\r
diff --git a/babel/tads2.c b/babel/tads2.c
new file mode 100644 (file)
index 0000000..87c1fcc
--- /dev/null
@@ -0,0 +1,100 @@
+/* \r
+ *   tads2.c - Treaty of Babel module for Tads 2 files\r
+ *   \r
+ *   This file depends on treaty_builder.h\r
+ *   \r
+ *   This file is public domain, but note that any changes to this file may\r
+ *   render it noncompliant with the Treaty of Babel\r
+ *   \r
+ *   Modified\r
+ *.   04/15/2006 LRRaszewski - Separated tads2.c and tads3.c \r
+ *.   04/08/2006 LRRaszewski - changed babel API calls to threadsafe versions\r
+ *.   04/08/2006 MJRoberts  - initial implementation\r
+ */\r
+\r
+#define FORMAT tads2\r
+#define HOME_PAGE "http://www.tads.org"\r
+#define FORMAT_EXT ".gam"\r
+\r
+\r
+#include "treaty_builder.h"\r
+#include "tads.h"\r
+\r
+#define T2_SIGNATURE "TADS2 bin\012\015\032"\r
+\r
+#ifndef FALSE\r
+#define FALSE 0\r
+#endif\r
+#ifndef TRUE\r
+#define TRUE 1\r
+#endif\r
+\r
+/*\r
+ *   get a story file's IFID\r
+ */\r
+static int32 get_story_file_IFID(void *story_file, int32 extent,\r
+                                 char *output, int32 output_extent)\r
+{\r
+    /* use the common tads IFID extractor/generator */\r
+    return tads_get_story_file_IFID(story_file, extent,\r
+                                    output, output_extent);\r
+}\r
+\r
+/*\r
+ *   determine if a given story file is one of ours \r
+ */\r
+static int32 claim_story_file(void *story_file, int32 extent)\r
+{\r
+    /* check our signature */\r
+    if (tads_match_sig(story_file, extent, T2_SIGNATURE))\r
+        return VALID_STORY_FILE_RV;\r
+\r
+    /* not one of ours */\r
+    return INVALID_STORY_FILE_RV;\r
+}\r
+\r
+/*\r
+ *   Get the size of the iFiction metadata for the game \r
+ */\r
+static int32 get_story_file_metadata_extent(void *story_file, int32 extent)\r
+{\r
+    /* use the common tads iFiction synthesizer */\r
+    return tads_get_story_file_metadata_extent(story_file, extent);\r
+}\r
+\r
+/*\r
+ *   Get the iFiction metadata for the game\r
+ */\r
+static int32 get_story_file_metadata(void *story_file, int32 extent,\r
+                                     char *buf, int32 bufsize)\r
+{\r
+    /* use the common tads iFiction synthesizer */\r
+    return tads_get_story_file_metadata(story_file, extent, buf,  bufsize);\r
+}\r
+\r
+static int32 get_story_file_cover_extent(void *story_file, int32 story_len)\r
+{\r
+    /* use the common tads cover file extractor */\r
+    return tads_get_story_file_cover_extent(story_file, story_len);\r
+}\r
+\r
+/*\r
+ *   Get the format of the cover art \r
+ */\r
+static int32 get_story_file_cover_format(void *story_file, int32 story_len)\r
+{\r
+    /* use the common tads cover file extractor */\r
+    return tads_get_story_file_cover_format(story_file, story_len);\r
+}\r
+\r
+/*\r
+ *   Get the cover art data \r
+ */\r
+static int32 get_story_file_cover(void *story_file, int32 story_len,\r
+                                  void *outbuf, int32 output_extent)\r
+{\r
+    /* use the common tads cover file extractor */\r
+    return tads_get_story_file_cover(story_file, story_len,\r
+                                     outbuf, output_extent);\r
+}\r
+\r
diff --git a/babel/tads3.c b/babel/tads3.c
new file mode 100644 (file)
index 0000000..23d8fb5
--- /dev/null
@@ -0,0 +1,100 @@
+/* \r
+ *   tads3.c - Treaty of Babel module for Tads 3 files\r
+ *   \r
+ *   This file depends on treaty_builder.h\r
+ *   \r
+ *   This file is public domain, but note that any changes to this file may\r
+ *   render it noncompliant with the Treaty of Babel\r
+ *   \r
+ *   Modified\r
+ *.   04/15/2006 LRRaszewski - Separated tads2.c and tads3.c \r
+ *.   04/08/2006 LRRaszewski - changed babel API calls to threadsafe versions\r
+ *.   04/08/2006 MJRoberts  - initial implementation\r
+ */\r
+\r
+#define FORMAT tads3\r
+#define HOME_PAGE "http://www.tads.org"\r
+#define FORMAT_EXT ".t3"\r
+\r
+\r
+#include "treaty_builder.h"\r
+#include "tads.h"\r
+\r
+#define T3_SIGNATURE "T3-image\015\012\032"\r
+\r
+#ifndef FALSE\r
+#define FALSE 0\r
+#endif\r
+#ifndef TRUE\r
+#define TRUE 1\r
+#endif\r
+\r
+/*\r
+ *   get a story file's IFID \r
+ */\r
+static int32 get_story_file_IFID(void *story_file, int32 extent,\r
+                                 char *output, int32 output_extent)\r
+{\r
+    /* use the common tads IFID extractor/generator */\r
+    return tads_get_story_file_IFID(story_file, extent,\r
+                                    output, output_extent);\r
+}\r
+\r
+/*\r
+ *   determine if a given story file is one of ours \r
+ */\r
+static int32 claim_story_file(void *story_file, int32 extent)\r
+{\r
+    /* check our signature */\r
+    if (tads_match_sig(story_file, extent, T3_SIGNATURE))\r
+        return VALID_STORY_FILE_RV;\r
+\r
+    /* not one of ours */\r
+    return INVALID_STORY_FILE_RV;\r
+}\r
+\r
+/*\r
+ *   Get the size of the iFiction metadata for the game \r
+ */\r
+static int32 get_story_file_metadata_extent(void *story_file, int32 extent)\r
+{\r
+    /* use the common tads iFiction synthesizer */\r
+    return tads_get_story_file_metadata_extent(story_file, extent);\r
+}\r
+\r
+/*\r
+ *   Get the iFiction metadata for the game\r
+ */\r
+static int32 get_story_file_metadata(void *story_file, int32 extent,\r
+                                     char *buf, int32 bufsize)\r
+{\r
+    /* use the common tads iFiction synthesizer */\r
+    return tads_get_story_file_metadata(story_file, extent, buf,  bufsize);\r
+}\r
+\r
+static int32 get_story_file_cover_extent(void *story_file, int32 story_len)\r
+{\r
+    /* use the common tads cover file extractor */\r
+    return tads_get_story_file_cover_extent(story_file, story_len);\r
+}\r
+\r
+/*\r
+ *   Get the format of the cover art \r
+ */\r
+static int32 get_story_file_cover_format(void *story_file, int32 story_len)\r
+{\r
+    /* use the common tads cover file extractor */\r
+    return tads_get_story_file_cover_format(story_file, story_len);\r
+}\r
+\r
+/*\r
+ *   Get the cover art data \r
+ */\r
+static int32 get_story_file_cover(void *story_file, int32 story_len,\r
+                                  void *outbuf, int32 output_extent)\r
+{\r
+    /* use the common tads cover file extractor */\r
+    return tads_get_story_file_cover(story_file, story_len,\r
+                                     outbuf, output_extent);\r
+}\r
+\r
diff --git a/babel/treaty.h b/babel/treaty.h
new file mode 100644 (file)
index 0000000..7155553
--- /dev/null
@@ -0,0 +1,91 @@
+/*  treaty.h    Header file for Treaty of Babel compliant format modules\r
+ *  By L. Ross Raszewski\r
+ *  Version 3b\r
+ *\r
+ *  This file is public domain, but please note that derived versions\r
+ *  may not not be compliant with the Treaty of Babel.\r
+ *\r
+ *  It would be wise for derived works to change the value of\r
+ *  TREATY_COMPLIANCE to reflect deviations\r
+ */\r
+\r
+#ifndef TREATY_H\r
+\r
+#define TREATY_H\r
+\r
+#define TREATY_COMPLIANCE "Treaty of Babel revision 7"\r
+#define TREATY_VERSION "r7"\r
+\r
+/* return codes */\r
+#define NO_REPLY_RV                    0\r
+#define INVALID_STORY_FILE_RV          -1\r
+#define UNAVAILABLE_RV                 -2\r
+#define INVALID_USAGE_RV               -3\r
+#define INCOMPLETE_REPLY_RV            -4\r
+#define VALID_STORY_FILE_RV            1\r
+\r
+#define PNG_COVER_FORMAT                1\r
+#define JPEG_COVER_FORMAT               2\r
+\r
+/* Treaty bitmasks.  These are not required by the treaty, but are here\r
+   as a convenience.\r
+*/\r
+#define TREATY_SELECTOR_INPUT           0x100\r
+#define TREATY_SELECTOR_OUTPUT          0x200\r
+#define TREATY_SELECTOR_NUMBER          0xFF\r
+\r
+#define TREATY_CONTAINER_SELECTOR       0x400\r
+\r
+/* Treaty selectors */\r
+#define GET_HOME_PAGE_SEL                       0x201\r
+#define GET_FORMAT_NAME_SEL                     0x202\r
+#define GET_FILE_EXTENSIONS_SEL                 0x203\r
+#define CLAIM_STORY_FILE_SEL                    0x104\r
+#define GET_STORY_FILE_METADATA_EXTENT_SEL      0x105\r
+#define GET_STORY_FILE_COVER_EXTENT_SEL         0x106\r
+#define GET_STORY_FILE_COVER_FORMAT_SEL         0x107\r
+#define GET_STORY_FILE_IFID_SEL                 0x308\r
+#define GET_STORY_FILE_METADATA_SEL             0x309\r
+#define GET_STORY_FILE_COVER_SEL                0x30A\r
+#define GET_STORY_FILE_EXTENSION_SEL            0x30B\r
+\r
+/* Container selectors */\r
+#define CONTAINER_GET_STORY_FORMAT_SEL                0x710\r
+#define CONTAINER_GET_STORY_EXTENT_SEL          0x511\r
+#define CONTAINER_GET_STORY_FILE_SEL            0x711\r
+\r
+\r
+\r
+\r
+/* Other magic size limits */\r
+#define TREATY_MINIMUM_EXTENT           512\r
+\r
+\r
+#include <limits.h>\r
+\r
+/* 32-bit integer types */\r
+#ifndef VAX\r
+#if   SCHAR_MAX >= 0x7FFFFFFFL && SCHAR_MIN <= -0x7FFFFFFFL\r
+      typedef signed char       int32;\r
+#elif SHRT_MAX >= 0x7FFFFFFFL  && SHRT_MIN <= -0x7FFFFFFFL\r
+      typedef signed short int  int32;\r
+#elif INT_MAX >= 0x7FFFFFFFL   && INT_MIN <= -0x7FFFFFFFL\r
+      typedef signed int        int32;\r
+#elif LONG_MAX >= 0x7FFFFFFFL  && LONG_MIN <= -0x7FFFFFFFL\r
+      typedef signed long int   int32;\r
+#else\r
+#error No type large enough to support 32-bit integers.\r
+#endif\r
+#else\r
+      /*  VAX C does not provide these limit constants, contrary to ANSI  */\r
+      typedef int int32;\r
+#endif\r
+\r
+\r
+\r
+/* Pointer to treaty function.  Treaty functions must follow this prototype */\r
+\r
+typedef int32 (*TREATY)(int32 selector, void *, int32, void *, int32);\r
+\r
+#endif\r
+\r
diff --git a/babel/treaty_builder.h b/babel/treaty_builder.h
new file mode 100644 (file)
index 0000000..d52ae3e
--- /dev/null
@@ -0,0 +1,180 @@
+/* treaty_builder.h common macros to build a treaty module\r
+ *\r
+ * 2006 By L. Ross Raszewski\r
+ *\r
+ * This file is public domain, but be aware that any changes to it may\r
+ * cause it to cease to be compliant with the Treaty of Babel.\r
+ *\r
+ * This file depends on treaty.h\r
+ *\r
+ * The purpose of this file is to simplify the building of a treaty\r
+ * module.  It automatically generates a generic treaty function.\r
+ *\r
+ * Usage:\r
+ *\r
+ * #define the following values:\r
+ *  FORMAT              The treaty name of the format\r
+ *  HOME_PAGE           A string containing the URL of the format home page\r
+ *  FORMAT_EXT          A string containing a comma separated list of common\r
+ *                       extensions for game files in this format\r
+ *  NO_METADATA         If the format does not support metadata\r
+ *  NO_COVER            If the format does not support cover art\r
+ *  CUSTOM_EXTENSION    If game files should not always use the first listed extension\r
+ *\r
+ * (Note: Formats which support metadata and cover art via a container should\r
+ * define NO_METADATA and NO_COVER as container support is handled separately)\r
+ *\r
+ * #include "treaty_builder.h"\r
+ * Define the following functions:\r
+ *   static int32 get_story_file_IFID(void *, int32, char *, int32);\r
+ *   static int32 claim_story_file(void *, int32);\r
+ * Define the following functions if NO_METADATA is not defined:\r
+ *   static int32 get_story_file_metadata_extent(void *, int32);\r
+ *   static int32 get_story_file_metadata(void *, int32, char *, int32);\r
+ * Define the following functions if NO_COVER is not defined\r
+ *   static int32 get_story_file_cover_extent(void *, int32);\r
+ *   static int32 get_story_file_cover_format(void *, int32);\r
+ *   static int32 get_story_file_cover(void *, int32, void *, int32);\r
+ * Define the following if CUSTOM_EXTENSION is defined\r
+ *   static int32 get_story_file_extension(void *, int32, char *, int32);\r
+ *\r
+ * The two-parameter functions take the story file and story file extent\r
+ * as parameters.  The four-parameter ones also take the output\r
+ * buffer and its extent.  They perform the corresponding task to the\r
+ * similarly-named selector.\r
+ *\r
+ * This file also defines the macro ASSERT_OUTPUT_SIZE(x) which\r
+ * returns INVALID_USAGE_RV if output_extent is less than x.\r
+ *\r
+ * #define CONTAINER_FORMAT  before inclusion to generate a container\r
+ * module.  A container module should define three additional functions:\r
+ *    static int32 get_story_format(void *, int32, char *, int32);\r
+ *    static int32 get_story_extent(void *, int32);\r
+ *    static int32 get_story_file(void *, int32, void *, int32);\r
+ *\r
+ */\r
+\r
+#ifndef TREATY_BUILDER\r
+#define TREATY_BUILDER\r
+\r
+#include "treaty.h"\r
+#include <string.h>\r
+\r
+#define ASSERT_OUTPUT_SIZE(x) do { if (output_extent < (x)) return INVALID_USAGE_RV; } while (0)\r
+\r
+#ifndef NO_METADATA\r
+static int32 get_story_file_metadata_extent(void *, int32);\r
+static int32 get_story_file_metadata(void *, int32, char *, int32);\r
+#endif\r
+#ifndef NO_COVER\r
+static int32 get_story_file_cover_extent(void *, int32);\r
+static int32 get_story_file_cover_format(void *, int32);\r
+static int32 get_story_file_cover(void *, int32, void *, int32);\r
+#endif\r
+static int32 get_story_file_IFID(void *, int32, char *, int32);\r
+static int32 claim_story_file(void *, int32);\r
+#ifdef CONTAINER_FORMAT\r
+static int32 get_story_file(void *, int32, void *, int32);\r
+static int32 get_story_format(void *, int32, char *, int32);\r
+static int32 get_story_extent(void *, int32);\r
+#endif\r
+#ifdef CUSTOM_EXTENSION\r
+static int32 get_story_file_extension(void *, int32, char *, int32);\r
+#else\r
+#include <stdio.h>\r
+static int32 get_story_file_extension(void *sf, int32 extent, char *out, int32 output_extent)\r
+{\r
+ int i;\r
+\r
+ if (!sf || !extent) return INVALID_STORY_FILE_RV;\r
+\r
+ for(i=0;FORMAT_EXT[i] && FORMAT_EXT[i]!=',';i++);\r
+ ASSERT_OUTPUT_SIZE(i+1);\r
+ memcpy(out,FORMAT_EXT,i);\r
+ out[i]=0;\r
+ return strlen(out);\r
+}\r
+\r
+#endif\r
+\r
+#define TREATY_FUNCTION(X)  DEEP_TREATY_FUNCTION(X)\r
+#define DEEP_TREATY_FUNCTION(X) X ## _treaty\r
+#define dSTRFRY(X) #X\r
+#define STRFRY(X) dSTRFRY(X)\r
+\r
+int32 TREATY_FUNCTION(FORMAT)(int32 selector,\r
+                   void *story_file, int32 extent,\r
+                   void *output, int32 output_extent)\r
+{\r
+ int32 ll, csf;\r
+ if ((TREATY_SELECTOR_INPUT & selector) &&\r
+     (csf=claim_story_file(story_file, extent))<NO_REPLY_RV)\r
+         return INVALID_STORY_FILE_RV;\r
\r
+\r
+ if ((TREATY_SELECTOR_OUTPUT & selector) &&\r
+     (output_extent ==0 ||\r
+     output==NULL))\r
+        return INVALID_USAGE_RV;\r
+ switch(selector)\r
+ {\r
+  case GET_HOME_PAGE_SEL:\r
+                ASSERT_OUTPUT_SIZE((signed) strlen(HOME_PAGE)+1);\r
+                strcpy((char *) output,HOME_PAGE);\r
+                return NO_REPLY_RV;\r
+  case GET_FORMAT_NAME_SEL:\r
+                ASSERT_OUTPUT_SIZE(512);\r
+                strncpy((char *) output,STRFRY(FORMAT),output_extent-1);\r
+                return NO_REPLY_RV;\r
+  case GET_FILE_EXTENSIONS_SEL:\r
+                ll=(strlen(FORMAT_EXT)+1) < 512 ? (strlen(FORMAT_EXT)+1):512;\r
+                ASSERT_OUTPUT_SIZE(ll);\r
+                strncpy((char *) output, FORMAT_EXT, output_extent);\r
+                return NO_REPLY_RV;\r
+  case CLAIM_STORY_FILE_SEL:\r
+                return csf;\r
+  case GET_STORY_FILE_METADATA_EXTENT_SEL:\r
+#ifndef NO_METADATA\r
+                return get_story_file_metadata_extent(story_file, extent);\r
+#endif\r
+  case GET_STORY_FILE_METADATA_SEL:\r
+#ifndef NO_METADATA\r
+                return get_story_file_metadata(story_file, extent, (char *)output, output_extent);\r
+#else\r
+                return NO_REPLY_RV;\r
+#endif\r
+  case GET_STORY_FILE_COVER_EXTENT_SEL:\r
+#ifndef NO_COVER\r
+                return get_story_file_cover_extent(story_file, extent);\r
+#endif\r
+  case GET_STORY_FILE_COVER_FORMAT_SEL:\r
+#ifndef NO_COVER\r
+                return get_story_file_cover_format(story_file, extent);\r
+#endif\r
+  case GET_STORY_FILE_COVER_SEL:\r
+#ifndef NO_COVER\r
+                return get_story_file_cover(story_file, extent, output, output_extent);\r
+#else\r
+                return NO_REPLY_RV;\r
+#endif\r
+  case GET_STORY_FILE_IFID_SEL:\r
+                return get_story_file_IFID(story_file, extent, (char *)output, output_extent);\r
+  case GET_STORY_FILE_EXTENSION_SEL:\r
+                return get_story_file_extension(story_file, extent, (char *)output, output_extent);\r
+#ifdef CONTAINER_FORMAT\r
+  case CONTAINER_GET_STORY_FORMAT_SEL:\r
+                return get_story_format(story_file, extent, (char *)output, output_extent);\r
+  case CONTAINER_GET_STORY_EXTENT_SEL:\r
+                return get_story_extent(story_file, extent);\r
+  case CONTAINER_GET_STORY_FILE_SEL:\r
+                return get_story_file(story_file, extent, output, output_extent);\r
+#endif\r
+\r
+ }\r
+ return UNAVAILABLE_RV;\r
+}\r
+\r
+\r
+#else\r
+#error "treaty_builder should be used as most once in any source file";\r
+#endif\r
diff --git a/babel/zcode.c b/babel/zcode.c
new file mode 100644 (file)
index 0000000..fb1d6ba
--- /dev/null
@@ -0,0 +1,97 @@
+/* zcode.c  Treaty of Babel module for Z-code files\r
+ * 2006 By L. Ross Raszewski\r
+ *\r
+ * This file depends on treaty_builder.h\r
+ *\r
+ * This file is public domain, but note that any changes to this file\r
+ * may render it noncompliant with the Treaty of Babel\r
+ */\r
+\r
+#define FORMAT zcode\r
+#define HOME_PAGE "http://www.inform-fiction.org"\r
+#define FORMAT_EXT ".z3,.z4,.z5,.z6,.z7,.z8"\r
+#define NO_METADATA\r
+#define NO_COVER\r
+#define CUSTOM_EXTENSION\r
+#include "treaty_builder.h"\r
+#include <ctype.h>\r
+#include <stdio.h>\r
+\r
+static int32 get_story_file_IFID(void *story_file, int32 extent, char *output, int32 output_extent)\r
+{\r
+ int32 i,j;\r
+ char ser[7];\r
+ char buffer[32];\r
+\r
+\r
+ if (extent<0x1D) return INVALID_STORY_FILE_RV;\r
+ memcpy(ser, (char *) story_file+0x12, 6);\r
+ ser[6]=0;\r
+ /* Detect vintage story files */\r
+ if (!(ser[0]=='8' || ser[0]=='9' ||\r
+     (ser[0]=='0' && ser[1]>='0' && ser[1]<='5')))\r
+ {\r
+  for(i=0;i<extent;i++) if (memcmp((char *)story_file+i,"UUID://",7)==0) break;\r
+  if (i<extent) /* Found explicit IFID */\r
+  {\r
+   for(j=i+7;j<extent && ((char *)story_file)[j]!='/';j++);\r
+   if (j<extent)\r
+   {\r
+    i+=7;\r
+    ASSERT_OUTPUT_SIZE(j-i);\r
+    memcpy(output,(char *)story_file+i,j-i);\r
+    output[j-i]=0;\r
+    return 1;\r
+   }\r
+  }\r
+ }\r
+ /* Did not find intact IFID.  Build one */\r
+ i=((unsigned char *)story_file)[2] << 8 |((unsigned char *)story_file)[3];\r
+ for(j=0;j<6;j++)\r
+  if (!isalnum(ser[j])) ser[j]='-';\r
+\r
+ j=((unsigned char *)story_file)[0x1C] << 8 |((unsigned char *)story_file)[0x1D];\r
+\r
+ if (strcmp(ser,"000000") && isdigit(ser[0]) && ser[0]!='8')\r
+  sprintf(buffer,"ZCODE-%d-%s-%04X",i,ser,j);\r
+ else\r
+  sprintf(buffer,"ZCODE-%d-%s",i,ser);\r
+\r
+ ASSERT_OUTPUT_SIZE((signed) strlen(buffer)+1);\r
+ strcpy((char *)output,buffer);\r
+ return 1;\r
+\r
+}\r
+\r
+static int32 read_zint(unsigned char *sf)\r
+{\r
+ return ((int32)sf[0] << 8) | ((int32) sf[1]);\r
+\r
+}\r
+static int32 claim_story_file(void *story_file, int32 extent)\r
+{\r
+ unsigned char *sf=(unsigned char *)story_file;\r
+ int32 i,j;\r
+ if (extent<0x3c ||\r
+     sf[0] < 1 ||\r
+     sf[0] > 8\r
+    ) return INVALID_STORY_FILE_RV;\r
+ for(i=4;i<=14;i+=2)\r
+ {\r
+  j=read_zint(sf+i);\r
+  if (j>extent || j < 0x40) return INVALID_STORY_FILE_RV;\r
+ }\r
+\r
+ return VALID_STORY_FILE_RV;\r
+}\r
+static int32 get_story_file_extension(void *sf, int32 extent, char *out, int32 output_extent)\r
+{\r
+ int v;\r
+ if (!extent) return INVALID_STORY_FILE_RV;\r
+ v= ((char *) sf)[0];\r
+ if (v>9) ASSERT_OUTPUT_SIZE(5);\r
+ else ASSERT_OUTPUT_SIZE(4);\r
+ sprintf(out,".z%d",v);\r
+ return 3+(v>9);\r
+\r
+}\r
index 9e73d1155372d449d84c34b3d40875d7dabe61cc..3fb5d0196b8f1c485ed22fdb7d3a536d0a68a348 100644 (file)
@@ -137,6 +137,7 @@ AC_SUBST(CHIMARA_LIBS)
 PKG_CHECK_MODULES([TEST], [
        gtk+-2.0 >= $GTK_REQUIRED_VERSION
        gmodule-2.0 >= $GLIB_REQUIRED_VERSION
+       libgda-4.0
 ])
 
 # GStreamer plugins needed to run library
@@ -188,6 +189,7 @@ docs/reference/Makefile
 docs/reference/version.xml
 docs/reference/build-selector-table.pl
 po/Makefile.in
+babel/Makefile
 ])
 
 # Do it
diff --git a/tests/Aotearoa.gblorb b/tests/Aotearoa.gblorb
new file mode 100644 (file)
index 0000000..3db124a
Binary files /dev/null and b/tests/Aotearoa.gblorb differ
index 6d1217662a2392fbb77d70a012d6d9acb5b8ce80..c8ae301164154a7bd652fc108d7c628a1d358da8 100644 (file)
@@ -10,7 +10,7 @@ TEST_PLUGIN_LIBTOOL_FLAGS = \
        -export-symbols-regex "^glk_main$$" \
        -rpath $(abs_builddir)
 
-noinst_PROGRAMS = test-multisession glulxercise plugin-loader test-close
+noinst_PROGRAMS = test-multisession glulxercise plugin-loader test-close babeltest
 
 test_multisession_SOURCES = test-multisession.c
 test_multisession_CFLAGS = @TEST_CFLAGS@ $(AM_CFLAGS)
@@ -29,6 +29,10 @@ test_close_SOURCES = test-close.c
 test_close_CFLAGS = @TEST_CFLAGS@ $(AM_CFLAGS)
 test_close_LDADD = @TEST_LIBS@ $(top_builddir)/libchimara/libchimara.la
 
+babeltest_SOURCES = babeltest.c
+babeltest_CFLAGS = @TEST_CFLAGS@ $(AM_CFLAGS)
+babeltest_LDADD = @TEST_LIBS@ $(top_builddir)/babel/libbabel_functions.la  $(top_builddir)/babel/libbabel.la $(top_builddir)/babel/libifiction.la
+
 noinst_LTLIBRARIES = first.la model.la gridtest.la splittest.la multiwin.la \
        styletest.la soundtest.la test-userstyle.la fileio.la
 
diff --git a/tests/babeltest.c b/tests/babeltest.c
new file mode 100644 (file)
index 0000000..6daba85
--- /dev/null
@@ -0,0 +1,177 @@
+#include "babel/babel_handler.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <malloc.h>
+#include <glib.h>
+#include <glib/gprintf.h>
+#include <libgda/libgda.h>
+#include <sql-parser/gda-sql-parser.h>
+
+typedef struct _metadata {
+       const gchar *element_name;
+       gchar *ifid;
+       gchar *title;
+       gchar *author;
+       gchar *year;
+} metadata;
+
+void start_element(
+       GMarkupParseContext *context,
+       const gchar *element_name,
+       const gchar **attribute_names,
+       const gchar **attribute_values,
+       gpointer data,
+       GError **error)
+{
+       metadata *md = (metadata*) data;
+       md->element_name = element_name;
+
+       if( !strcmp(element_name, "ifindex") ) {
+               md->ifid = g_strdup("");
+               md->title = g_strdup("");
+               md->author = g_strdup("");
+               md->year = g_strdup("");
+       }
+}
+
+void text(
+       GMarkupParseContext *context,
+       const gchar *text,
+       gsize text_len,
+       gpointer data,
+       GError **error)
+{
+       metadata *md = (metadata*) data;
+       gchar *stripped_text;
+
+       if( !strcmp(md->element_name, "ifid") ) {
+               stripped_text = g_strstrip( g_strndup(text, text_len) );
+               md->ifid = g_strconcat(md->ifid, stripped_text, NULL);
+               g_free(stripped_text);
+       }
+       else if( !strcmp(md->element_name, "title") ) {
+               stripped_text = g_strstrip( g_strndup(text, text_len) );
+               md->title = g_strconcat(md->title, stripped_text, NULL);
+               g_free(stripped_text);
+       }
+       else if( !strcmp(md->element_name, "author") ) {
+               stripped_text = g_strstrip( g_strndup(text, text_len) );
+               md->author = g_strconcat(md->author, stripped_text, NULL);
+               g_free(stripped_text);
+       }
+       else if( !strcmp(md->element_name, "firstpublished") ) {
+               stripped_text = g_strstrip( g_strndup(text, text_len) );
+               md->year = g_strconcat(md->year, stripped_text, NULL);
+               g_free(stripped_text);
+       }
+}
+
+void end_element(
+       GMarkupParseContext *context,
+       const gchar *element_name,
+       gpointer data,
+       GError **error)
+{
+       if( !strcmp(element_name, "ifindex") ) {
+               metadata *md = (metadata*) data;
+               printf("IFID: %s\nTitle: %s\nAuthor: %s\nYear: %s\n", md->ifid, md->title, md->author, md->year);
+       }
+}
+
+/*
+ * run a non SELECT command and stops if an error occurs
+ */
+void
+run_sql_non_select(GdaConnection *cnc, const gchar *sql)
+{
+       GdaStatement *stmt;
+       GError *error = NULL;
+       gint nrows;
+       const gchar *remain;
+       GdaSqlParser *parser;
+
+       parser = g_object_get_data(G_OBJECT(cnc), "parser");
+       stmt = gda_sql_parser_parse_string(parser, sql, &remain, &error);
+       if(remain) 
+               g_print ("REMAINS: %s\n", remain);
+
+       nrows = gda_connection_statement_execute_non_select(cnc, stmt, NULL, NULL, &error);
+       if(nrows == -1)
+               g_error("NON SELECT error: %s\n", error && error->message ? error->message : "no detail");
+       g_object_unref(stmt);
+}
+
+int main(int argc, char **argv) {
+       if(argc < 2) {
+               fprintf(stderr, "Usage: %s <story file>\n", argv[0]);
+               return 1;
+       }
+
+       babel_init(argv[1]);
+       int len = babel_treaty(GET_STORY_FILE_METADATA_EXTENT_SEL, NULL, 0);
+       if(len == 0) {
+               printf("No metadata found.\n");
+               babel_release();
+               return 0;
+       }
+
+       gchar *buffer = malloc(len * sizeof(gchar));
+       babel_treaty(GET_STORY_FILE_METADATA_SEL, buffer, len);
+       g_strchomp(buffer);
+       len = strlen(buffer);
+
+       metadata data;
+       GMarkupParser xml_parser = {start_element, end_element, text, NULL, NULL};
+       GMarkupParseContext *context = g_markup_parse_context_new(&xml_parser, 0, &data, NULL);
+
+       GError *err = NULL;
+       if( g_markup_parse_context_parse(context, buffer, len, &err) == FALSE ) {
+               fprintf(stderr, "Metadata parse failed: %s\n", err->message);
+       }
+
+       free(buffer);
+       g_markup_parse_context_free(context);
+       babel_release();
+
+       // Open DB connection
+       GdaConnection *cnc;
+       GdaSqlParser *sql_parser;
+
+       gda_init();
+       cnc = gda_connection_open_from_string("SQLite", "DB_DIR=.;DB_NAME=library", NULL, GDA_CONNECTION_OPTIONS_NONE, &err);
+       if(!cnc) {
+               fprintf(stderr, "Could not open connection to SQLite database in library.db file: %s\n", err && err->message ? err->message : "No details");
+               return 1;
+       }
+
+       sql_parser = gda_connection_create_parser(cnc);
+       if(!sql_parser) // cnc does not provide its own parser, use default one
+               sql_parser = gda_sql_parser_new();
+
+       g_object_set_data_full(G_OBJECT(cnc), "parser", sql_parser, g_object_unref);
+       
+       // Create stories table
+       run_sql_non_select(cnc, "DROP TABLE IF EXISTS stories");
+       run_sql_non_select(cnc, "CREATE TABLE stories (ifid text not null primary key, title text, author text, year integer)");
+
+       // Populate the table
+       GValue *v1, *v2, *v3, *v4;
+       v1 = gda_value_new_from_string(data.ifid, G_TYPE_STRING);
+       v2 = gda_value_new_from_string(data.title, G_TYPE_STRING);
+       v3 = gda_value_new_from_string(data.author, G_TYPE_STRING);
+       v4 = gda_value_new_from_string(data.year, G_TYPE_UINT);
+
+       if( !gda_insert_row_into_table(cnc, "stories", &err, "ifid", v1, "title", v2, "author", v3, "year", v4, NULL) ) {
+               g_error("Could not INSERT data into the 'stories' table: %s\n", err && err->message ? err->message : "No details");
+               return 1;
+       }
+
+       gda_value_free(v1);
+       gda_value_free(v2);
+       gda_value_free(v3);
+       gda_value_free(v4);
+
+       gda_connection_close(cnc);
+       return 0;
+}