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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+# 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+#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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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,>,ifiction_null_eh,NULL);\r
+ if (gt.target){ if (gt.output) free(gt.output); return NULL; }\r
+ return gt.output;\r
+}\r
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/*
+ 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));
+}
--- /dev/null
+/*
+ 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 */
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* \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 '<', '>', and '&',\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, "<");\r
+ ++p, --len;\r
+ break;\r
+\r
+ case '>':\r
+ write_ifiction_z(ctx, ">");\r
+ ++p, --len;\r
+ break;\r
+\r
+ case '&':\r
+ write_ifiction_z(ctx, "&");\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
--- /dev/null
+/*\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
--- /dev/null
+/* \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
--- /dev/null
+/* \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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
--- /dev/null
+/* 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
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
docs/reference/version.xml
docs/reference/build-selector-table.pl
po/Makefile.in
+babel/Makefile
])
# Do it
-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)
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
--- /dev/null
+#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;
+}