diff options
author | Aaron Giles <aaron@aarongiles.com> | 2007-12-17 15:19:59 +0000 |
---|---|---|
committer | Aaron Giles <aaron@aarongiles.com> | 2007-12-17 15:19:59 +0000 |
commit | 7b77f1218624ea26dbb2efd85a19f795f5d4e02e (patch) | |
tree | 19209304095572b4fd61c2a2d6a5aa75c4e471ad /src/lib | |
parent | 3da7f476068b3ffef713218ba2fc1bd5030f2c38 (diff) |
Initial checkin of MAME 0.121.mame0121
Diffstat (limited to 'src/lib')
89 files changed, 44773 insertions, 0 deletions
diff --git a/src/lib/expat/ascii.h b/src/lib/expat/ascii.h new file mode 100644 index 00000000000..337e5bb7ea0 --- /dev/null +++ b/src/lib/expat/ascii.h @@ -0,0 +1,85 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#define ASCII_A 0x41 +#define ASCII_B 0x42 +#define ASCII_C 0x43 +#define ASCII_D 0x44 +#define ASCII_E 0x45 +#define ASCII_F 0x46 +#define ASCII_G 0x47 +#define ASCII_H 0x48 +#define ASCII_I 0x49 +#define ASCII_J 0x4A +#define ASCII_K 0x4B +#define ASCII_L 0x4C +#define ASCII_M 0x4D +#define ASCII_N 0x4E +#define ASCII_O 0x4F +#define ASCII_P 0x50 +#define ASCII_Q 0x51 +#define ASCII_R 0x52 +#define ASCII_S 0x53 +#define ASCII_T 0x54 +#define ASCII_U 0x55 +#define ASCII_V 0x56 +#define ASCII_W 0x57 +#define ASCII_X 0x58 +#define ASCII_Y 0x59 +#define ASCII_Z 0x5A + +#define ASCII_a 0x61 +#define ASCII_b 0x62 +#define ASCII_c 0x63 +#define ASCII_d 0x64 +#define ASCII_e 0x65 +#define ASCII_f 0x66 +#define ASCII_g 0x67 +#define ASCII_h 0x68 +#define ASCII_i 0x69 +#define ASCII_j 0x6A +#define ASCII_k 0x6B +#define ASCII_l 0x6C +#define ASCII_m 0x6D +#define ASCII_n 0x6E +#define ASCII_o 0x6F +#define ASCII_p 0x70 +#define ASCII_q 0x71 +#define ASCII_r 0x72 +#define ASCII_s 0x73 +#define ASCII_t 0x74 +#define ASCII_u 0x75 +#define ASCII_v 0x76 +#define ASCII_w 0x77 +#define ASCII_x 0x78 +#define ASCII_y 0x79 +#define ASCII_z 0x7A + +#define ASCII_0 0x30 +#define ASCII_1 0x31 +#define ASCII_2 0x32 +#define ASCII_3 0x33 +#define ASCII_4 0x34 +#define ASCII_5 0x35 +#define ASCII_6 0x36 +#define ASCII_7 0x37 +#define ASCII_8 0x38 +#define ASCII_9 0x39 + +#define ASCII_TAB 0x09 +#define ASCII_SPACE 0x20 +#define ASCII_EXCL 0x21 +#define ASCII_QUOT 0x22 +#define ASCII_AMP 0x26 +#define ASCII_APOS 0x27 +#define ASCII_MINUS 0x2D +#define ASCII_PERIOD 0x2E +#define ASCII_COLON 0x3A +#define ASCII_SEMI 0x3B +#define ASCII_LT 0x3C +#define ASCII_EQUALS 0x3D +#define ASCII_GT 0x3E +#define ASCII_LSQB 0x5B +#define ASCII_RSQB 0x5D +#define ASCII_UNDERSCORE 0x5F diff --git a/src/lib/expat/asciitab.h b/src/lib/expat/asciitab.h new file mode 100644 index 00000000000..79a15c28ca1 --- /dev/null +++ b/src/lib/expat/asciitab.h @@ -0,0 +1,36 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML, +/* 0x0C */ BT_NONXML, BT_CR, BT_NONXML, BT_NONXML, +/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM, +/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS, +/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS, +/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL, +/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI, +/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST, +/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB, +/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT, +/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER, diff --git a/src/lib/expat/expat.h b/src/lib/expat/expat.h new file mode 100644 index 00000000000..00ddbcee4ad --- /dev/null +++ b/src/lib/expat/expat.h @@ -0,0 +1,1013 @@ +/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#ifndef Expat_INCLUDED +#define Expat_INCLUDED 1 + +#ifdef __VMS +/* 0 1 2 3 0 1 2 3 + 1234567890123456789012345678901 1234567890123456789012345678901 */ +#define XML_SetProcessingInstructionHandler XML_SetProcessingInstrHandler +#define XML_SetUnparsedEntityDeclHandler XML_SetUnparsedEntDeclHandler +#define XML_SetStartNamespaceDeclHandler XML_SetStartNamespcDeclHandler +#define XML_SetExternalEntityRefHandlerArg XML_SetExternalEntRefHandlerArg +#endif + +#include <stdlib.h> +#include "expat_external.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct XML_ParserStruct; +typedef struct XML_ParserStruct *XML_Parser; + +/* Should this be defined using stdbool.h when C99 is available? */ +typedef unsigned char XML_Bool; +#define XML_TRUE ((XML_Bool) 1) +#define XML_FALSE ((XML_Bool) 0) + +/* The XML_Status enum gives the possible return values for several + API functions. The preprocessor #defines are included so this + stanza can be added to code that still needs to support older + versions of Expat 1.95.x: + + #ifndef XML_STATUS_OK + #define XML_STATUS_OK 1 + #define XML_STATUS_ERROR 0 + #endif + + Otherwise, the #define hackery is quite ugly and would have been + dropped. +*/ +enum XML_Status { + XML_STATUS_ERROR = 0, +#define XML_STATUS_ERROR XML_STATUS_ERROR + XML_STATUS_OK = 1, +#define XML_STATUS_OK XML_STATUS_OK + XML_STATUS_SUSPENDED = 2 +#define XML_STATUS_SUSPENDED XML_STATUS_SUSPENDED +}; + +enum XML_Error { + XML_ERROR_NONE, + XML_ERROR_NO_MEMORY, + XML_ERROR_SYNTAX, + XML_ERROR_NO_ELEMENTS, + XML_ERROR_INVALID_TOKEN, + XML_ERROR_UNCLOSED_TOKEN, + XML_ERROR_PARTIAL_CHAR, + XML_ERROR_TAG_MISMATCH, + XML_ERROR_DUPLICATE_ATTRIBUTE, + XML_ERROR_JUNK_AFTER_DOC_ELEMENT, + XML_ERROR_PARAM_ENTITY_REF, + XML_ERROR_UNDEFINED_ENTITY, + XML_ERROR_RECURSIVE_ENTITY_REF, + XML_ERROR_ASYNC_ENTITY, + XML_ERROR_BAD_CHAR_REF, + XML_ERROR_BINARY_ENTITY_REF, + XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF, + XML_ERROR_MISPLACED_XML_PI, + XML_ERROR_UNKNOWN_ENCODING, + XML_ERROR_INCORRECT_ENCODING, + XML_ERROR_UNCLOSED_CDATA_SECTION, + XML_ERROR_EXTERNAL_ENTITY_HANDLING, + XML_ERROR_NOT_STANDALONE, + XML_ERROR_UNEXPECTED_STATE, + XML_ERROR_ENTITY_DECLARED_IN_PE, + XML_ERROR_FEATURE_REQUIRES_XML_DTD, + XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING, + /* Added in 1.95.7. */ + XML_ERROR_UNBOUND_PREFIX, + /* Added in 1.95.8. */ + XML_ERROR_UNDECLARING_PREFIX, + XML_ERROR_INCOMPLETE_PE, + XML_ERROR_XML_DECL, + XML_ERROR_TEXT_DECL, + XML_ERROR_PUBLICID, + XML_ERROR_SUSPENDED, + XML_ERROR_NOT_SUSPENDED, + XML_ERROR_ABORTED, + XML_ERROR_FINISHED, + XML_ERROR_SUSPEND_PE, + /* Added in 2.0. */ + XML_ERROR_RESERVED_PREFIX_XML, + XML_ERROR_RESERVED_PREFIX_XMLNS, + XML_ERROR_RESERVED_NAMESPACE_URI +}; + +enum XML_Content_Type { + XML_CTYPE_EMPTY = 1, + XML_CTYPE_ANY, + XML_CTYPE_MIXED, + XML_CTYPE_NAME, + XML_CTYPE_CHOICE, + XML_CTYPE_SEQ +}; + +enum XML_Content_Quant { + XML_CQUANT_NONE, + XML_CQUANT_OPT, + XML_CQUANT_REP, + XML_CQUANT_PLUS +}; + +/* If type == XML_CTYPE_EMPTY or XML_CTYPE_ANY, then quant will be + XML_CQUANT_NONE, and the other fields will be zero or NULL. + If type == XML_CTYPE_MIXED, then quant will be NONE or REP and + numchildren will contain number of elements that may be mixed in + and children point to an array of XML_Content cells that will be + all of XML_CTYPE_NAME type with no quantification. + + If type == XML_CTYPE_NAME, then the name points to the name, and + the numchildren field will be zero and children will be NULL. The + quant fields indicates any quantifiers placed on the name. + + CHOICE and SEQ will have name NULL, the number of children in + numchildren and children will point, recursively, to an array + of XML_Content cells. + + The EMPTY, ANY, and MIXED types will only occur at top level. +*/ + +typedef struct XML_cp XML_Content; + +struct XML_cp { + enum XML_Content_Type type; + enum XML_Content_Quant quant; + XML_Char * name; + unsigned int numchildren; + XML_Content * children; +}; + + +/* This is called for an element declaration. See above for + description of the model argument. It's the caller's responsibility + to free model when finished with it. +*/ +typedef void (XMLCALL *XML_ElementDeclHandler) (void *userData, + const XML_Char *name, + XML_Content *model); + +XMLPARSEAPI(void) +XML_SetElementDeclHandler(XML_Parser parser, + XML_ElementDeclHandler eldecl); + +/* The Attlist declaration handler is called for *each* attribute. So + a single Attlist declaration with multiple attributes declared will + generate multiple calls to this handler. The "default" parameter + may be NULL in the case of the "#IMPLIED" or "#REQUIRED" + keyword. The "isrequired" parameter will be true and the default + value will be NULL in the case of "#REQUIRED". If "isrequired" is + true and default is non-NULL, then this is a "#FIXED" default. +*/ +typedef void (XMLCALL *XML_AttlistDeclHandler) ( + void *userData, + const XML_Char *elname, + const XML_Char *attname, + const XML_Char *att_type, + const XML_Char *dflt, + int isrequired); + +XMLPARSEAPI(void) +XML_SetAttlistDeclHandler(XML_Parser parser, + XML_AttlistDeclHandler attdecl); + +/* The XML declaration handler is called for *both* XML declarations + and text declarations. The way to distinguish is that the version + parameter will be NULL for text declarations. The encoding + parameter may be NULL for XML declarations. The standalone + parameter will be -1, 0, or 1 indicating respectively that there + was no standalone parameter in the declaration, that it was given + as no, or that it was given as yes. +*/ +typedef void (XMLCALL *XML_XmlDeclHandler) (void *userData, + const XML_Char *version, + const XML_Char *encoding, + int standalone); + +XMLPARSEAPI(void) +XML_SetXmlDeclHandler(XML_Parser parser, + XML_XmlDeclHandler xmldecl); + + +typedef struct { + void *(*malloc_fcn)(size_t size); + void *(*realloc_fcn)(void *ptr, size_t size); + void (*free_fcn)(void *ptr); +} XML_Memory_Handling_Suite; + +/* Constructs a new parser; encoding is the encoding specified by the + external protocol or NULL if there is none specified. +*/ +XMLPARSEAPI(XML_Parser) +XML_ParserCreate(const XML_Char *encoding); + +/* Constructs a new parser and namespace processor. Element type + names and attribute names that belong to a namespace will be + expanded; unprefixed attribute names are never expanded; unprefixed + element type names are expanded only if there is a default + namespace. The expanded name is the concatenation of the namespace + URI, the namespace separator character, and the local part of the + name. If the namespace separator is '\0' then the namespace URI + and the local part will be concatenated without any separator. + It is a programming error to use the separator '\0' with namespace + triplets (see XML_SetReturnNSTriplet). +*/ +XMLPARSEAPI(XML_Parser) +XML_ParserCreateNS(const XML_Char *encoding, XML_Char namespaceSeparator); + + +/* Constructs a new parser using the memory management suite referred to + by memsuite. If memsuite is NULL, then use the standard library memory + suite. If namespaceSeparator is non-NULL it creates a parser with + namespace processing as described above. The character pointed at + will serve as the namespace separator. + + All further memory operations used for the created parser will come from + the given suite. +*/ +XMLPARSEAPI(XML_Parser) +XML_ParserCreate_MM(const XML_Char *encoding, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *namespaceSeparator); + +/* Prepare a parser object to be re-used. This is particularly + valuable when memory allocation overhead is disproportionatly high, + such as when a large number of small documnents need to be parsed. + All handlers are cleared from the parser, except for the + unknownEncodingHandler. The parser's external state is re-initialized + except for the values of ns and ns_triplets. + + Added in Expat 1.95.3. +*/ +XMLPARSEAPI(XML_Bool) +XML_ParserReset(XML_Parser parser, const XML_Char *encoding); + +/* atts is array of name/value pairs, terminated by 0; + names and values are 0 terminated. +*/ +typedef void (XMLCALL *XML_StartElementHandler) (void *userData, + const XML_Char *name, + const XML_Char **atts); + +typedef void (XMLCALL *XML_EndElementHandler) (void *userData, + const XML_Char *name); + + +/* s is not 0 terminated. */ +typedef void (XMLCALL *XML_CharacterDataHandler) (void *userData, + const XML_Char *s, + int len); + +/* target and data are 0 terminated */ +typedef void (XMLCALL *XML_ProcessingInstructionHandler) ( + void *userData, + const XML_Char *target, + const XML_Char *data); + +/* data is 0 terminated */ +typedef void (XMLCALL *XML_CommentHandler) (void *userData, + const XML_Char *data); + +typedef void (XMLCALL *XML_StartCdataSectionHandler) (void *userData); +typedef void (XMLCALL *XML_EndCdataSectionHandler) (void *userData); + +/* This is called for any characters in the XML document for which + there is no applicable handler. This includes both characters that + are part of markup which is of a kind that is not reported + (comments, markup declarations), or characters that are part of a + construct which could be reported but for which no handler has been + supplied. The characters are passed exactly as they were in the XML + document except that they will be encoded in UTF-8 or UTF-16. + Line boundaries are not normalized. Note that a byte order mark + character is not passed to the default handler. There are no + guarantees about how characters are divided between calls to the + default handler: for example, a comment might be split between + multiple calls. +*/ +typedef void (XMLCALL *XML_DefaultHandler) (void *userData, + const XML_Char *s, + int len); + +/* This is called for the start of the DOCTYPE declaration, before + any DTD or internal subset is parsed. +*/ +typedef void (XMLCALL *XML_StartDoctypeDeclHandler) ( + void *userData, + const XML_Char *doctypeName, + const XML_Char *sysid, + const XML_Char *pubid, + int has_internal_subset); + +/* This is called for the start of the DOCTYPE declaration when the + closing > is encountered, but after processing any external + subset. +*/ +typedef void (XMLCALL *XML_EndDoctypeDeclHandler)(void *userData); + +/* This is called for entity declarations. The is_parameter_entity + argument will be non-zero if the entity is a parameter entity, zero + otherwise. + + For internal entities (<!ENTITY foo "bar">), value will + be non-NULL and systemId, publicID, and notationName will be NULL. + The value string is NOT nul-terminated; the length is provided in + the value_length argument. Since it is legal to have zero-length + values, do not use this argument to test for internal entities. + + For external entities, value will be NULL and systemId will be + non-NULL. The publicId argument will be NULL unless a public + identifier was provided. The notationName argument will have a + non-NULL value only for unparsed entity declarations. + + Note that is_parameter_entity can't be changed to XML_Bool, since + that would break binary compatibility. +*/ +typedef void (XMLCALL *XML_EntityDeclHandler) ( + void *userData, + const XML_Char *entityName, + int is_parameter_entity, + const XML_Char *value, + int value_length, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId, + const XML_Char *notationName); + +XMLPARSEAPI(void) +XML_SetEntityDeclHandler(XML_Parser parser, + XML_EntityDeclHandler handler); + +/* OBSOLETE -- OBSOLETE -- OBSOLETE + This handler has been superceded by the EntityDeclHandler above. + It is provided here for backward compatibility. + + This is called for a declaration of an unparsed (NDATA) entity. + The base argument is whatever was set by XML_SetBase. The + entityName, systemId and notationName arguments will never be + NULL. The other arguments may be. +*/ +typedef void (XMLCALL *XML_UnparsedEntityDeclHandler) ( + void *userData, + const XML_Char *entityName, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId, + const XML_Char *notationName); + +/* This is called for a declaration of notation. The base argument is + whatever was set by XML_SetBase. The notationName will never be + NULL. The other arguments can be. +*/ +typedef void (XMLCALL *XML_NotationDeclHandler) ( + void *userData, + const XML_Char *notationName, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId); + +/* When namespace processing is enabled, these are called once for + each namespace declaration. The call to the start and end element + handlers occur between the calls to the start and end namespace + declaration handlers. For an xmlns attribute, prefix will be + NULL. For an xmlns="" attribute, uri will be NULL. +*/ +typedef void (XMLCALL *XML_StartNamespaceDeclHandler) ( + void *userData, + const XML_Char *prefix, + const XML_Char *uri); + +typedef void (XMLCALL *XML_EndNamespaceDeclHandler) ( + void *userData, + const XML_Char *prefix); + +/* This is called if the document is not standalone, that is, it has an + external subset or a reference to a parameter entity, but does not + have standalone="yes". If this handler returns XML_STATUS_ERROR, + then processing will not continue, and the parser will return a + XML_ERROR_NOT_STANDALONE error. + If parameter entity parsing is enabled, then in addition to the + conditions above this handler will only be called if the referenced + entity was actually read. +*/ +typedef int (XMLCALL *XML_NotStandaloneHandler) (void *userData); + +/* This is called for a reference to an external parsed general + entity. The referenced entity is not automatically parsed. The + application can parse it immediately or later using + XML_ExternalEntityParserCreate. + + The parser argument is the parser parsing the entity containing the + reference; it can be passed as the parser argument to + XML_ExternalEntityParserCreate. The systemId argument is the + system identifier as specified in the entity declaration; it will + not be NULL. + + The base argument is the system identifier that should be used as + the base for resolving systemId if systemId was relative; this is + set by XML_SetBase; it may be NULL. + + The publicId argument is the public identifier as specified in the + entity declaration, or NULL if none was specified; the whitespace + in the public identifier will have been normalized as required by + the XML spec. + + The context argument specifies the parsing context in the format + expected by the context argument to XML_ExternalEntityParserCreate; + context is valid only until the handler returns, so if the + referenced entity is to be parsed later, it must be copied. + context is NULL only when the entity is a parameter entity. + + The handler should return XML_STATUS_ERROR if processing should not + continue because of a fatal error in the handling of the external + entity. In this case the calling parser will return an + XML_ERROR_EXTERNAL_ENTITY_HANDLING error. + + Note that unlike other handlers the first argument is the parser, + not userData. +*/ +typedef int (XMLCALL *XML_ExternalEntityRefHandler) ( + XML_Parser parser, + const XML_Char *context, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId); + +/* This is called in two situations: + 1) An entity reference is encountered for which no declaration + has been read *and* this is not an error. + 2) An internal entity reference is read, but not expanded, because + XML_SetDefaultHandler has been called. + Note: skipped parameter entities in declarations and skipped general + entities in attribute values cannot be reported, because + the event would be out of sync with the reporting of the + declarations or attribute values +*/ +typedef void (XMLCALL *XML_SkippedEntityHandler) ( + void *userData, + const XML_Char *entityName, + int is_parameter_entity); + +/* This structure is filled in by the XML_UnknownEncodingHandler to + provide information to the parser about encodings that are unknown + to the parser. + + The map[b] member gives information about byte sequences whose + first byte is b. + + If map[b] is c where c is >= 0, then b by itself encodes the + Unicode scalar value c. + + If map[b] is -1, then the byte sequence is malformed. + + If map[b] is -n, where n >= 2, then b is the first byte of an + n-byte sequence that encodes a single Unicode scalar value. + + The data member will be passed as the first argument to the convert + function. + + The convert function is used to convert multibyte sequences; s will + point to a n-byte sequence where map[(unsigned char)*s] == -n. The + convert function must return the Unicode scalar value represented + by this byte sequence or -1 if the byte sequence is malformed. + + The convert function may be NULL if the encoding is a single-byte + encoding, that is if map[b] >= -1 for all bytes b. + + When the parser is finished with the encoding, then if release is + not NULL, it will call release passing it the data member; once + release has been called, the convert function will not be called + again. + + Expat places certain restrictions on the encodings that are supported + using this mechanism. + + 1. Every ASCII character that can appear in a well-formed XML document, + other than the characters + + $@\^`{}~ + + must be represented by a single byte, and that byte must be the + same byte that represents that character in ASCII. + + 2. No character may require more than 4 bytes to encode. + + 3. All characters encoded must have Unicode scalar values <= + 0xFFFF, (i.e., characters that would be encoded by surrogates in + UTF-16 are not allowed). Note that this restriction doesn't + apply to the built-in support for UTF-8 and UTF-16. + + 4. No Unicode character may be encoded by more than one distinct + sequence of bytes. +*/ +typedef struct { + int map[256]; + void *data; + int (XMLCALL *convert)(void *data, const char *s); + void (XMLCALL *release)(void *data); +} XML_Encoding; + +/* This is called for an encoding that is unknown to the parser. + + The encodingHandlerData argument is that which was passed as the + second argument to XML_SetUnknownEncodingHandler. + + The name argument gives the name of the encoding as specified in + the encoding declaration. + + If the callback can provide information about the encoding, it must + fill in the XML_Encoding structure, and return XML_STATUS_OK. + Otherwise it must return XML_STATUS_ERROR. + + If info does not describe a suitable encoding, then the parser will + return an XML_UNKNOWN_ENCODING error. +*/ +typedef int (XMLCALL *XML_UnknownEncodingHandler) ( + void *encodingHandlerData, + const XML_Char *name, + XML_Encoding *info); + +XMLPARSEAPI(void) +XML_SetElementHandler(XML_Parser parser, + XML_StartElementHandler start, + XML_EndElementHandler end); + +XMLPARSEAPI(void) +XML_SetStartElementHandler(XML_Parser parser, + XML_StartElementHandler handler); + +XMLPARSEAPI(void) +XML_SetEndElementHandler(XML_Parser parser, + XML_EndElementHandler handler); + +XMLPARSEAPI(void) +XML_SetCharacterDataHandler(XML_Parser parser, + XML_CharacterDataHandler handler); + +XMLPARSEAPI(void) +XML_SetProcessingInstructionHandler(XML_Parser parser, + XML_ProcessingInstructionHandler handler); +XMLPARSEAPI(void) +XML_SetCommentHandler(XML_Parser parser, + XML_CommentHandler handler); + +XMLPARSEAPI(void) +XML_SetCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start, + XML_EndCdataSectionHandler end); + +XMLPARSEAPI(void) +XML_SetStartCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start); + +XMLPARSEAPI(void) +XML_SetEndCdataSectionHandler(XML_Parser parser, + XML_EndCdataSectionHandler end); + +/* This sets the default handler and also inhibits expansion of + internal entities. These entity references will be passed to the + default handler, or to the skipped entity handler, if one is set. +*/ +XMLPARSEAPI(void) +XML_SetDefaultHandler(XML_Parser parser, + XML_DefaultHandler handler); + +/* This sets the default handler but does not inhibit expansion of + internal entities. The entity reference will not be passed to the + default handler. +*/ +XMLPARSEAPI(void) +XML_SetDefaultHandlerExpand(XML_Parser parser, + XML_DefaultHandler handler); + +XMLPARSEAPI(void) +XML_SetDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start, + XML_EndDoctypeDeclHandler end); + +XMLPARSEAPI(void) +XML_SetStartDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start); + +XMLPARSEAPI(void) +XML_SetEndDoctypeDeclHandler(XML_Parser parser, + XML_EndDoctypeDeclHandler end); + +XMLPARSEAPI(void) +XML_SetUnparsedEntityDeclHandler(XML_Parser parser, + XML_UnparsedEntityDeclHandler handler); + +XMLPARSEAPI(void) +XML_SetNotationDeclHandler(XML_Parser parser, + XML_NotationDeclHandler handler); + +XMLPARSEAPI(void) +XML_SetNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start, + XML_EndNamespaceDeclHandler end); + +XMLPARSEAPI(void) +XML_SetStartNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start); + +XMLPARSEAPI(void) +XML_SetEndNamespaceDeclHandler(XML_Parser parser, + XML_EndNamespaceDeclHandler end); + +XMLPARSEAPI(void) +XML_SetNotStandaloneHandler(XML_Parser parser, + XML_NotStandaloneHandler handler); + +XMLPARSEAPI(void) +XML_SetExternalEntityRefHandler(XML_Parser parser, + XML_ExternalEntityRefHandler handler); + +/* If a non-NULL value for arg is specified here, then it will be + passed as the first argument to the external entity ref handler + instead of the parser object. +*/ +XMLPARSEAPI(void) +XML_SetExternalEntityRefHandlerArg(XML_Parser parser, + void *arg); + +XMLPARSEAPI(void) +XML_SetSkippedEntityHandler(XML_Parser parser, + XML_SkippedEntityHandler handler); + +XMLPARSEAPI(void) +XML_SetUnknownEncodingHandler(XML_Parser parser, + XML_UnknownEncodingHandler handler, + void *encodingHandlerData); + +/* This can be called within a handler for a start element, end + element, processing instruction or character data. It causes the + corresponding markup to be passed to the default handler. +*/ +XMLPARSEAPI(void) +XML_DefaultCurrent(XML_Parser parser); + +/* If do_nst is non-zero, and namespace processing is in effect, and + a name has a prefix (i.e. an explicit namespace qualifier) then + that name is returned as a triplet in a single string separated by + the separator character specified when the parser was created: URI + + sep + local_name + sep + prefix. + + If do_nst is zero, then namespace information is returned in the + default manner (URI + sep + local_name) whether or not the name + has a prefix. + + Note: Calling XML_SetReturnNSTriplet after XML_Parse or + XML_ParseBuffer has no effect. +*/ + +XMLPARSEAPI(void) +XML_SetReturnNSTriplet(XML_Parser parser, int do_nst); + +/* This value is passed as the userData argument to callbacks. */ +XMLPARSEAPI(void) +XML_SetUserData(XML_Parser parser, void *userData); + +/* Returns the last value set by XML_SetUserData or NULL. */ +#define XML_GetUserData(parser) (*(void **)(parser)) + +/* This is equivalent to supplying an encoding argument to + XML_ParserCreate. On success XML_SetEncoding returns non-zero, + zero otherwise. + Note: Calling XML_SetEncoding after XML_Parse or XML_ParseBuffer + has no effect and returns XML_STATUS_ERROR. +*/ +XMLPARSEAPI(enum XML_Status) +XML_SetEncoding(XML_Parser parser, const XML_Char *encoding); + +/* If this function is called, then the parser will be passed as the + first argument to callbacks instead of userData. The userData will + still be accessible using XML_GetUserData. +*/ +XMLPARSEAPI(void) +XML_UseParserAsHandlerArg(XML_Parser parser); + +/* If useDTD == XML_TRUE is passed to this function, then the parser + will assume that there is an external subset, even if none is + specified in the document. In such a case the parser will call the + externalEntityRefHandler with a value of NULL for the systemId + argument (the publicId and context arguments will be NULL as well). + Note: For the purpose of checking WFC: Entity Declared, passing + useDTD == XML_TRUE will make the parser behave as if the document + had a DTD with an external subset. + Note: If this function is called, then this must be done before + the first call to XML_Parse or XML_ParseBuffer, since it will + have no effect after that. Returns + XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING. + Note: If the document does not have a DOCTYPE declaration at all, + then startDoctypeDeclHandler and endDoctypeDeclHandler will not + be called, despite an external subset being parsed. + Note: If XML_DTD is not defined when Expat is compiled, returns + XML_ERROR_FEATURE_REQUIRES_XML_DTD. +*/ +XMLPARSEAPI(enum XML_Error) +XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD); + + +/* Sets the base to be used for resolving relative URIs in system + identifiers in declarations. Resolving relative identifiers is + left to the application: this value will be passed through as the + base argument to the XML_ExternalEntityRefHandler, + XML_NotationDeclHandler and XML_UnparsedEntityDeclHandler. The base + argument will be copied. Returns XML_STATUS_ERROR if out of memory, + XML_STATUS_OK otherwise. +*/ +XMLPARSEAPI(enum XML_Status) +XML_SetBase(XML_Parser parser, const XML_Char *base); + +XMLPARSEAPI(const XML_Char *) +XML_GetBase(XML_Parser parser); + +/* Returns the number of the attribute/value pairs passed in last call + to the XML_StartElementHandler that were specified in the start-tag + rather than defaulted. Each attribute/value pair counts as 2; thus + this correspondds to an index into the atts array passed to the + XML_StartElementHandler. +*/ +XMLPARSEAPI(int) +XML_GetSpecifiedAttributeCount(XML_Parser parser); + +/* Returns the index of the ID attribute passed in the last call to + XML_StartElementHandler, or -1 if there is no ID attribute. Each + attribute/value pair counts as 2; thus this correspondds to an + index into the atts array passed to the XML_StartElementHandler. +*/ +XMLPARSEAPI(int) +XML_GetIdAttributeIndex(XML_Parser parser); + +/* Parses some input. Returns XML_STATUS_ERROR if a fatal error is + detected. The last call to XML_Parse must have isFinal true; len + may be zero for this call (or any other). + + Though the return values for these functions has always been + described as a Boolean value, the implementation, at least for the + 1.95.x series, has always returned exactly one of the XML_Status + values. +*/ +XMLPARSEAPI(enum XML_Status) +XML_Parse(XML_Parser parser, const char *s, int len, int isFinal); + +XMLPARSEAPI(void *) +XML_GetBuffer(XML_Parser parser, int len); + +XMLPARSEAPI(enum XML_Status) +XML_ParseBuffer(XML_Parser parser, int len, int isFinal); + +/* Stops parsing, causing XML_Parse() or XML_ParseBuffer() to return. + Must be called from within a call-back handler, except when aborting + (resumable = 0) an already suspended parser. Some call-backs may + still follow because they would otherwise get lost. Examples: + - endElementHandler() for empty elements when stopped in + startElementHandler(), + - endNameSpaceDeclHandler() when stopped in endElementHandler(), + and possibly others. + + Can be called from most handlers, including DTD related call-backs, + except when parsing an external parameter entity and resumable != 0. + Returns XML_STATUS_OK when successful, XML_STATUS_ERROR otherwise. + Possible error codes: + - XML_ERROR_SUSPENDED: when suspending an already suspended parser. + - XML_ERROR_FINISHED: when the parser has already finished. + - XML_ERROR_SUSPEND_PE: when suspending while parsing an external PE. + + When resumable != 0 (true) then parsing is suspended, that is, + XML_Parse() and XML_ParseBuffer() return XML_STATUS_SUSPENDED. + Otherwise, parsing is aborted, that is, XML_Parse() and XML_ParseBuffer() + return XML_STATUS_ERROR with error code XML_ERROR_ABORTED. + + *Note*: + This will be applied to the current parser instance only, that is, if + there is a parent parser then it will continue parsing when the + externalEntityRefHandler() returns. It is up to the implementation of + the externalEntityRefHandler() to call XML_StopParser() on the parent + parser (recursively), if one wants to stop parsing altogether. + + When suspended, parsing can be resumed by calling XML_ResumeParser(). +*/ +XMLPARSEAPI(enum XML_Status) +XML_StopParser(XML_Parser parser, XML_Bool resumable); + +/* Resumes parsing after it has been suspended with XML_StopParser(). + Must not be called from within a handler call-back. Returns same + status codes as XML_Parse() or XML_ParseBuffer(). + Additional error code XML_ERROR_NOT_SUSPENDED possible. + + *Note*: + This must be called on the most deeply nested child parser instance + first, and on its parent parser only after the child parser has finished, + to be applied recursively until the document entity's parser is restarted. + That is, the parent parser will not resume by itself and it is up to the + application to call XML_ResumeParser() on it at the appropriate moment. +*/ +XMLPARSEAPI(enum XML_Status) +XML_ResumeParser(XML_Parser parser); + +enum XML_Parsing { + XML_INITIALIZED, + XML_PARSING, + XML_FINISHED, + XML_SUSPENDED +}; + +typedef struct { + enum XML_Parsing parsing; + XML_Bool finalBuffer; +} XML_ParsingStatus; + +/* Returns status of parser with respect to being initialized, parsing, + finished, or suspended and processing the final buffer. + XXX XML_Parse() and XML_ParseBuffer() should return XML_ParsingStatus, + XXX with XML_FINISHED_OK or XML_FINISHED_ERROR replacing XML_FINISHED +*/ +XMLPARSEAPI(void) +XML_GetParsingStatus(XML_Parser parser, XML_ParsingStatus *status); + +/* Creates an XML_Parser object that can parse an external general + entity; context is a '\0'-terminated string specifying the parse + context; encoding is a '\0'-terminated string giving the name of + the externally specified encoding, or NULL if there is no + externally specified encoding. The context string consists of a + sequence of tokens separated by formfeeds (\f); a token consisting + of a name specifies that the general entity of the name is open; a + token of the form prefix=uri specifies the namespace for a + particular prefix; a token of the form =uri specifies the default + namespace. This can be called at any point after the first call to + an ExternalEntityRefHandler so longer as the parser has not yet + been freed. The new parser is completely independent and may + safely be used in a separate thread. The handlers and userData are + initialized from the parser argument. Returns NULL if out of memory. + Otherwise returns a new XML_Parser object. +*/ +XMLPARSEAPI(XML_Parser) +XML_ExternalEntityParserCreate(XML_Parser parser, + const XML_Char *context, + const XML_Char *encoding); + +enum XML_ParamEntityParsing { + XML_PARAM_ENTITY_PARSING_NEVER, + XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE, + XML_PARAM_ENTITY_PARSING_ALWAYS +}; + +/* Controls parsing of parameter entities (including the external DTD + subset). If parsing of parameter entities is enabled, then + references to external parameter entities (including the external + DTD subset) will be passed to the handler set with + XML_SetExternalEntityRefHandler. The context passed will be 0. + + Unlike external general entities, external parameter entities can + only be parsed synchronously. If the external parameter entity is + to be parsed, it must be parsed during the call to the external + entity ref handler: the complete sequence of + XML_ExternalEntityParserCreate, XML_Parse/XML_ParseBuffer and + XML_ParserFree calls must be made during this call. After + XML_ExternalEntityParserCreate has been called to create the parser + for the external parameter entity (context must be 0 for this + call), it is illegal to make any calls on the old parser until + XML_ParserFree has been called on the newly created parser. + If the library has been compiled without support for parameter + entity parsing (ie without XML_DTD being defined), then + XML_SetParamEntityParsing will return 0 if parsing of parameter + entities is requested; otherwise it will return non-zero. + Note: If XML_SetParamEntityParsing is called after XML_Parse or + XML_ParseBuffer, then it has no effect and will always return 0. +*/ +XMLPARSEAPI(int) +XML_SetParamEntityParsing(XML_Parser parser, + enum XML_ParamEntityParsing parsing); + +/* If XML_Parse or XML_ParseBuffer have returned XML_STATUS_ERROR, then + XML_GetErrorCode returns information about the error. +*/ +XMLPARSEAPI(enum XML_Error) +XML_GetErrorCode(XML_Parser parser); + +/* These functions return information about the current parse + location. They may be called from any callback called to report + some parse event; in this case the location is the location of the + first of the sequence of characters that generated the event. When + called from callbacks generated by declarations in the document + prologue, the location identified isn't as neatly defined, but will + be within the relevant markup. When called outside of the callback + functions, the position indicated will be just past the last parse + event (regardless of whether there was an associated callback). + + They may also be called after returning from a call to XML_Parse + or XML_ParseBuffer. If the return value is XML_STATUS_ERROR then + the location is the location of the character at which the error + was detected; otherwise the location is the location of the last + parse event, as described above. +*/ +XMLPARSEAPI(XML_Size) XML_GetCurrentLineNumber(XML_Parser parser); +XMLPARSEAPI(XML_Size) XML_GetCurrentColumnNumber(XML_Parser parser); +XMLPARSEAPI(XML_Index) XML_GetCurrentByteIndex(XML_Parser parser); + +/* Return the number of bytes in the current event. + Returns 0 if the event is in an internal entity. +*/ +XMLPARSEAPI(int) +XML_GetCurrentByteCount(XML_Parser parser); + +/* If XML_CONTEXT_BYTES is defined, returns the input buffer, sets + the integer pointed to by offset to the offset within this buffer + of the current parse position, and sets the integer pointed to by size + to the size of this buffer (the number of input bytes). Otherwise + returns a NULL pointer. Also returns a NULL pointer if a parse isn't + active. + + NOTE: The character pointer returned should not be used outside + the handler that makes the call. +*/ +XMLPARSEAPI(const char *) +XML_GetInputContext(XML_Parser parser, + int *offset, + int *size); + +/* For backwards compatibility with previous versions. */ +#define XML_GetErrorLineNumber XML_GetCurrentLineNumber +#define XML_GetErrorColumnNumber XML_GetCurrentColumnNumber +#define XML_GetErrorByteIndex XML_GetCurrentByteIndex + +/* Frees the content model passed to the element declaration handler */ +XMLPARSEAPI(void) +XML_FreeContentModel(XML_Parser parser, XML_Content *model); + +/* Exposing the memory handling functions used in Expat */ +XMLPARSEAPI(void *) +XML_MemMalloc(XML_Parser parser, size_t size); + +XMLPARSEAPI(void *) +XML_MemRealloc(XML_Parser parser, void *ptr, size_t size); + +XMLPARSEAPI(void) +XML_MemFree(XML_Parser parser, void *ptr); + +/* Frees memory used by the parser. */ +XMLPARSEAPI(void) +XML_ParserFree(XML_Parser parser); + +/* Returns a string describing the error. */ +XMLPARSEAPI(const XML_LChar *) +XML_ErrorString(enum XML_Error code); + +/* Return a string containing the version number of this expat */ +XMLPARSEAPI(const XML_LChar *) +XML_ExpatVersion(void); + +typedef struct { + int major; + int minor; + int micro; +} XML_Expat_Version; + +/* Return an XML_Expat_Version structure containing numeric version + number information for this version of expat. +*/ +XMLPARSEAPI(XML_Expat_Version) +XML_ExpatVersionInfo(void); + +/* Added in Expat 1.95.5. */ +enum XML_FeatureEnum { + XML_FEATURE_END = 0, + XML_FEATURE_UNICODE, + XML_FEATURE_UNICODE_WCHAR_T, + XML_FEATURE_DTD, + XML_FEATURE_CONTEXT_BYTES, + XML_FEATURE_MIN_SIZE, + XML_FEATURE_SIZEOF_XML_CHAR, + XML_FEATURE_SIZEOF_XML_LCHAR, + XML_FEATURE_NS + /* Additional features must be added to the end of this enum. */ +}; + +typedef struct { + enum XML_FeatureEnum feature; + const XML_LChar *name; + long int value; +} XML_Feature; + +XMLPARSEAPI(const XML_Feature *) +XML_GetFeatureList(void); + + +/* Expat follows the GNU/Linux convention of odd number minor version for + beta/development releases and even number minor version for stable + releases. Micro is bumped with each release, and set to 0 with each + change to major or minor version. +*/ +#define XML_MAJOR_VERSION 2 +#define XML_MINOR_VERSION 0 +#define XML_MICRO_VERSION 0 + +#ifdef __cplusplus +} +#endif + +#endif /* not Expat_INCLUDED */ diff --git a/src/lib/expat/expat_external.h b/src/lib/expat/expat_external.h new file mode 100644 index 00000000000..99c5b922cbf --- /dev/null +++ b/src/lib/expat/expat_external.h @@ -0,0 +1,115 @@ +/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#ifndef Expat_External_INCLUDED +#define Expat_External_INCLUDED 1 + +/* External API definitions */ + +#if defined(_MSC_EXTENSIONS) && !defined(__BEOS__) && !defined(__CYGWIN__) +#define XML_USE_MSC_EXTENSIONS 1 +#endif + +/* Expat tries very hard to make the API boundary very specifically + defined. There are two macros defined to control this boundary; + each of these can be defined before including this header to + achieve some different behavior, but doing so it not recommended or + tested frequently. + + XMLCALL - The calling convention to use for all calls across the + "library boundary." This will default to cdecl, and + try really hard to tell the compiler that's what we + want. + + XMLIMPORT - Whatever magic is needed to note that a function is + to be imported from a dynamically loaded library + (.dll, .so, or .sl, depending on your platform). + + The XMLCALL macro was added in Expat 1.95.7. The only one which is + expected to be directly useful in client code is XMLCALL. + + Note that on at least some Unix versions, the Expat library must be + compiled with the cdecl calling convention as the default since + system headers may assume the cdecl convention. +*/ +#ifndef XMLCALL +#if defined(XML_USE_MSC_EXTENSIONS) +#define XMLCALL __cdecl +#elif defined(__GNUC__) && defined(__i386) +#define XMLCALL __attribute__((cdecl)) +#else +/* For any platform which uses this definition and supports more than + one calling convention, we need to extend this definition to + declare the convention used on that platform, if it's possible to + do so. + + If this is the case for your platform, please file a bug report + with information on how to identify your platform via the C + pre-processor and how to specify the same calling convention as the + platform's malloc() implementation. +*/ +#define XMLCALL +#endif +#endif /* not defined XMLCALL */ + + +#if !defined(XML_STATIC) && !defined(XMLIMPORT) +#ifndef XML_BUILDING_EXPAT +/* using Expat from an application */ + +#ifdef XML_USE_MSC_EXTENSIONS +#define XMLIMPORT __declspec(dllimport) +#endif + +#endif +#endif /* not defined XML_STATIC */ + + +/* If we didn't define it above, define it away: */ +#ifndef XMLIMPORT +#define XMLIMPORT +#endif + + +#define XMLPARSEAPI(type) XMLIMPORT type XMLCALL + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef XML_UNICODE_WCHAR_T +#define XML_UNICODE +#endif + +#ifdef XML_UNICODE /* Information is UTF-16 encoded. */ +#ifdef XML_UNICODE_WCHAR_T +typedef wchar_t XML_Char; +typedef wchar_t XML_LChar; +#else +typedef unsigned short XML_Char; +typedef char XML_LChar; +#endif /* XML_UNICODE_WCHAR_T */ +#else /* Information is UTF-8 encoded. */ +typedef char XML_Char; +typedef char XML_LChar; +#endif /* XML_UNICODE */ + +#ifdef XML_LARGE_SIZE /* Use large integers for file/stream positions. */ +#if defined(XML_USE_MSC_EXTENSIONS) && _MSC_VER < 1400 +typedef __int64 XML_Index; +typedef unsigned __int64 XML_Size; +#else +typedef long long XML_Index; +typedef unsigned long long XML_Size; +#endif +#else +typedef long XML_Index; +typedef unsigned long XML_Size; +#endif /* XML_LARGE_SIZE */ + +#ifdef __cplusplus +} +#endif + +#endif /* not Expat_External_INCLUDED */ diff --git a/src/lib/expat/iasciitab.h b/src/lib/expat/iasciitab.h new file mode 100644 index 00000000000..24a1d5ccc9a --- /dev/null +++ b/src/lib/expat/iasciitab.h @@ -0,0 +1,37 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +/* Like asciitab.h, except that 0xD has code BT_S rather than BT_CR */ +/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML, +/* 0x0C */ BT_NONXML, BT_S, BT_NONXML, BT_NONXML, +/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM, +/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS, +/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS, +/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL, +/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI, +/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST, +/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB, +/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT, +/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER, diff --git a/src/lib/expat/intconfig.h b/src/lib/expat/intconfig.h new file mode 100644 index 00000000000..88092e6a006 --- /dev/null +++ b/src/lib/expat/intconfig.h @@ -0,0 +1,19 @@ +#ifndef INTCONFIG_H +#define INTCONFIG_H + +#include <stdlib.h> +#include <string.h> + +#define XML_NS 1 +#define XML_DTD 1 +#define XML_CONTEXT_BYTES 1024 + +#ifdef USE_LSB +#define BYTEORDER 1234 +#else +#define BYTEORDER 4321 +#endif + +#define HAVE_MEMMOVE + +#endif diff --git a/src/lib/expat/internal.h b/src/lib/expat/internal.h new file mode 100644 index 00000000000..ff056c659cd --- /dev/null +++ b/src/lib/expat/internal.h @@ -0,0 +1,73 @@ +/* internal.h + + Internal definitions used by Expat. This is not needed to compile + client code. + + The following calling convention macros are defined for frequently + called functions: + + FASTCALL - Used for those internal functions that have a simple + body and a low number of arguments and local variables. + + PTRCALL - Used for functions called though function pointers. + + PTRFASTCALL - Like PTRCALL, but for low number of arguments. + + inline - Used for selected internal functions for which inlining + may improve performance on some platforms. + + Note: Use of these macros is based on judgement, not hard rules, + and therefore subject to change. +*/ + +#if defined(__GNUC__) && defined(__i386__) +/* We'll use this version by default only where we know it helps. + + regparm() generates warnings on Solaris boxes. See SF bug #692878. + + Instability reported with egcs on a RedHat Linux 7.3. + Let's comment out: + #define FASTCALL __attribute__((stdcall, regparm(3))) + and let's try this: +*/ +#define FASTCALL __attribute__((regparm(3))) +#define PTRFASTCALL __attribute__((regparm(3))) +#endif + +/* Using __fastcall seems to have an unexpected negative effect under + MS VC++, especially for function pointers, so we won't use it for + now on that platform. It may be reconsidered for a future release + if it can be made more effective. + Likely reason: __fastcall on Windows is like stdcall, therefore + the compiler cannot perform stack optimizations for call clusters. +*/ + +/* Make sure all of these are defined if they aren't already. */ + +#ifndef FASTCALL +#define FASTCALL +#endif + +#ifndef PTRCALL +#define PTRCALL +#endif + +#ifndef PTRFASTCALL +#define PTRFASTCALL +#endif + +#ifndef XML_MIN_SIZE +#if !defined(__cplusplus) && !defined(inline) +#ifdef __GNUC__ +#define inline __inline +#endif /* __GNUC__ */ +#endif +#endif /* XML_MIN_SIZE */ + +#ifdef __cplusplus +#define inline inline +#else +#ifndef inline +#define inline +#endif +#endif diff --git a/src/lib/expat/latin1tab.h b/src/lib/expat/latin1tab.h new file mode 100644 index 00000000000..53c25d76b26 --- /dev/null +++ b/src/lib/expat/latin1tab.h @@ -0,0 +1,36 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +/* 0x80 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x84 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x88 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x8C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x90 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x94 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x98 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x9C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xA0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xA4 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xA8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER, +/* 0xAC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xB0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xB4 */ BT_OTHER, BT_NMSTRT, BT_OTHER, BT_NAME, +/* 0xB8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER, +/* 0xBC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xC0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xC4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xC8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xCC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xD0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xD4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0xD8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xDC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xE0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xE4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xE8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xEC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xF0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xF4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0xF8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xFC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, diff --git a/src/lib/expat/nametab.h b/src/lib/expat/nametab.h new file mode 100644 index 00000000000..b05e62c77a6 --- /dev/null +++ b/src/lib/expat/nametab.h @@ -0,0 +1,150 @@ +static const unsigned namingBitmap[] = { +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0x00000000, 0x04000000, 0x87FFFFFE, 0x07FFFFFE, +0x00000000, 0x00000000, 0xFF7FFFFF, 0xFF7FFFFF, +0xFFFFFFFF, 0x7FF3FFFF, 0xFFFFFDFE, 0x7FFFFFFF, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE00F, 0xFC31FFFF, +0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, +0xFFFFFFFF, 0xF80001FF, 0x00000003, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFD740, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD, +0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, +0xFFFF0003, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF, +0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE, +0x0000007F, 0x00000000, 0xFFFF0000, 0x000707FF, +0x00000000, 0x07FFFFFE, 0x000007FE, 0xFFFE0000, +0xFFFFFFFF, 0x7CFFFFFF, 0x002F7FFF, 0x00000060, +0xFFFFFFE0, 0x23FFFFFF, 0xFF000000, 0x00000003, +0xFFF99FE0, 0x03C5FDFF, 0xB0000000, 0x00030003, +0xFFF987E0, 0x036DFDFF, 0x5E000000, 0x001C0000, +0xFFFBAFE0, 0x23EDFDFF, 0x00000000, 0x00000001, +0xFFF99FE0, 0x23CDFDFF, 0xB0000000, 0x00000003, +0xD63DC7E0, 0x03BFC718, 0x00000000, 0x00000000, +0xFFFDDFE0, 0x03EFFDFF, 0x00000000, 0x00000003, +0xFFFDDFE0, 0x03EFFDFF, 0x40000000, 0x00000003, +0xFFFDDFE0, 0x03FFFDFF, 0x00000000, 0x00000003, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFE, 0x000D7FFF, 0x0000003F, 0x00000000, +0xFEF02596, 0x200D6CAE, 0x0000001F, 0x00000000, +0x00000000, 0x00000000, 0xFFFFFEFF, 0x000003FF, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0xFFFFFFFF, 0xFFFF003F, 0x007FFFFF, +0x0007DAED, 0x50000000, 0x82315001, 0x002C62AB, +0x40000000, 0xF580C900, 0x00000007, 0x02010800, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0x0FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x03FFFFFF, +0x3F3FFFFF, 0xFFFFFFFF, 0xAAFF3F3F, 0x3FFFFFFF, +0xFFFFFFFF, 0x5FDFFFFF, 0x0FCF1FDC, 0x1FDC1FFF, +0x00000000, 0x00004C40, 0x00000000, 0x00000000, +0x00000007, 0x00000000, 0x00000000, 0x00000000, +0x00000080, 0x000003FE, 0xFFFFFFFE, 0xFFFFFFFF, +0x001FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x07FFFFFF, +0xFFFFFFE0, 0x00001FFF, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0xFFFFFFFF, 0x0000003F, 0x00000000, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0xFFFFFFFF, 0x0000000F, 0x00000000, 0x00000000, +0x00000000, 0x07FF6000, 0x87FFFFFE, 0x07FFFFFE, +0x00000000, 0x00800000, 0xFF7FFFFF, 0xFF7FFFFF, +0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, +0xFFFFFFFF, 0xF80001FF, 0x00030003, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0x0000003F, 0x00000003, +0xFFFFD7C0, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD, +0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, +0xFFFF007B, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF, +0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE, +0xFFFE007F, 0xBBFFFFFB, 0xFFFF0016, 0x000707FF, +0x00000000, 0x07FFFFFE, 0x0007FFFF, 0xFFFF03FF, +0xFFFFFFFF, 0x7CFFFFFF, 0xFFEF7FFF, 0x03FF3DFF, +0xFFFFFFEE, 0xF3FFFFFF, 0xFF1E3FFF, 0x0000FFCF, +0xFFF99FEE, 0xD3C5FDFF, 0xB080399F, 0x0003FFCF, +0xFFF987E4, 0xD36DFDFF, 0x5E003987, 0x001FFFC0, +0xFFFBAFEE, 0xF3EDFDFF, 0x00003BBF, 0x0000FFC1, +0xFFF99FEE, 0xF3CDFDFF, 0xB0C0398F, 0x0000FFC3, +0xD63DC7EC, 0xC3BFC718, 0x00803DC7, 0x0000FF80, +0xFFFDDFEE, 0xC3EFFDFF, 0x00603DDF, 0x0000FFC3, +0xFFFDDFEC, 0xC3EFFDFF, 0x40603DDF, 0x0000FFC3, +0xFFFDDFEC, 0xC3FFFDFF, 0x00803DCF, 0x0000FFC3, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFE, 0x07FF7FFF, 0x03FF7FFF, 0x00000000, +0xFEF02596, 0x3BFF6CAE, 0x03FF3F5F, 0x00000000, +0x03000000, 0xC2A003FF, 0xFFFFFEFF, 0xFFFE03FF, +0xFEBF0FDF, 0x02FE3FFF, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x1FFF0000, 0x00000002, +0x000000A0, 0x003EFFFE, 0xFFFFFFFE, 0xFFFFFFFF, +0x661FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x77FFFFFF, +}; +static const unsigned char nmstrtPages[] = { +0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00, +0x00, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, +0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, +0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x15, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static const unsigned char namePages[] = { +0x19, 0x03, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x00, +0x00, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, +0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, +0x26, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x27, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; diff --git a/src/lib/expat/utf8tab.h b/src/lib/expat/utf8tab.h new file mode 100644 index 00000000000..7bb3e77603f --- /dev/null +++ b/src/lib/expat/utf8tab.h @@ -0,0 +1,37 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + + +/* 0x80 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x84 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x88 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x8C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x90 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x94 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x98 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x9C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xA0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xA4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xA8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xAC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xB0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xB4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xB8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xBC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xC0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xC4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xC8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xCC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xD0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xD4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xD8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xDC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xE0 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xE4 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xE8 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xEC */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xF0 */ BT_LEAD4, BT_LEAD4, BT_LEAD4, BT_LEAD4, +/* 0xF4 */ BT_LEAD4, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0xF8 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0xFC */ BT_NONXML, BT_NONXML, BT_MALFORM, BT_MALFORM, diff --git a/src/lib/expat/xmlparse.c b/src/lib/expat/xmlparse.c new file mode 100644 index 00000000000..ed9344ca8ff --- /dev/null +++ b/src/lib/expat/xmlparse.c @@ -0,0 +1,6258 @@ +/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#include <stddef.h> +#include <string.h> /* memset(), memcpy() */ +#include <assert.h> + +#define XML_BUILDING_EXPAT 1 + +#include "intconfig.h" + +#include "expat.h" + +#ifdef XML_UNICODE +#define XML_ENCODE_MAX XML_UTF16_ENCODE_MAX +#define XmlConvert XmlUtf16Convert +#define XmlGetInternalEncoding XmlGetUtf16InternalEncoding +#define XmlGetInternalEncodingNS XmlGetUtf16InternalEncodingNS +#define XmlEncode XmlUtf16Encode +#define MUST_CONVERT(enc, s) (!(enc)->isUtf16 || (((unsigned long)s) & 1)) +typedef unsigned short ICHAR; +#else +#define XML_ENCODE_MAX XML_UTF8_ENCODE_MAX +#define XmlConvert XmlUtf8Convert +#define XmlGetInternalEncoding XmlGetUtf8InternalEncoding +#define XmlGetInternalEncodingNS XmlGetUtf8InternalEncodingNS +#define XmlEncode XmlUtf8Encode +#define MUST_CONVERT(enc, s) (!(enc)->isUtf8) +typedef char ICHAR; +#endif + + +#ifndef XML_NS + +#define XmlInitEncodingNS XmlInitEncoding +#define XmlInitUnknownEncodingNS XmlInitUnknownEncoding +#undef XmlGetInternalEncodingNS +#define XmlGetInternalEncodingNS XmlGetInternalEncoding +#define XmlParseXmlDeclNS XmlParseXmlDecl + +#endif + +#ifdef XML_UNICODE + +#ifdef XML_UNICODE_WCHAR_T +#define XML_T(x) (const wchar_t)x +#define XML_L(x) L ## x +#else +#define XML_T(x) (const unsigned short)x +#define XML_L(x) x +#endif + +#else + +#define XML_T(x) x +#define XML_L(x) x + +#endif + +/* Round up n to be a multiple of sz, where sz is a power of 2. */ +#define ROUND_UP(n, sz) (((n) + ((sz) - 1)) & ~((sz) - 1)) + +/* Handle the case where memmove() doesn't exist. */ +#ifndef HAVE_MEMMOVE +#ifdef HAVE_BCOPY +#define memmove(d,s,l) bcopy((s),(d),(l)) +#else +#error memmove does not exist on this platform, nor is a substitute available +#endif /* HAVE_BCOPY */ +#endif /* HAVE_MEMMOVE */ + +#include "internal.h" +#include "xmltok.h" +#include "xmlrole.h" + +typedef const XML_Char *KEY; + +typedef struct { + KEY name; +} NAMED; + +typedef struct { + NAMED **v; + unsigned char power; + size_t size; + size_t used; + const XML_Memory_Handling_Suite *mem; +} HASH_TABLE; + +/* Basic character hash algorithm, taken from Python's string hash: + h = h * 1000003 ^ character, the constant being a prime number. + +*/ +#ifdef XML_UNICODE +#define CHAR_HASH(h, c) \ + (((h) * 0xF4243) ^ (unsigned short)(c)) +#else +#define CHAR_HASH(h, c) \ + (((h) * 0xF4243) ^ (unsigned char)(c)) +#endif + +/* For probing (after a collision) we need a step size relative prime + to the hash table size, which is a power of 2. We use double-hashing, + since we can calculate a second hash value cheaply by taking those bits + of the first hash value that were discarded (masked out) when the table + index was calculated: index = hash & mask, where mask = table->size - 1. + We limit the maximum step size to table->size / 4 (mask >> 2) and make + it odd, since odd numbers are always relative prime to a power of 2. +*/ +#define SECOND_HASH(hash, mask, power) \ + ((((hash) & ~(mask)) >> ((power) - 1)) & ((mask) >> 2)) +#define PROBE_STEP(hash, mask, power) \ + ((unsigned char)((SECOND_HASH(hash, mask, power)) | 1)) + +typedef struct { + NAMED **p; + NAMED **end; +} HASH_TABLE_ITER; + +#define INIT_TAG_BUF_SIZE 32 /* must be a multiple of sizeof(XML_Char) */ +#define INIT_DATA_BUF_SIZE 1024 +#define INIT_ATTS_SIZE 16 +#define INIT_ATTS_VERSION 0xFFFFFFFF +#define INIT_BLOCK_SIZE 1024 +#define INIT_BUFFER_SIZE 1024 + +#define EXPAND_SPARE 24 + +typedef struct binding { + struct prefix *prefix; + struct binding *nextTagBinding; + struct binding *prevPrefixBinding; + const struct attribute_id *attId; + XML_Char *uri; + int uriLen; + int uriAlloc; +} BINDING; + +typedef struct prefix { + const XML_Char *name; + BINDING *binding; +} PREFIX; + +typedef struct { + const XML_Char *str; + const XML_Char *localPart; + const XML_Char *prefix; + int strLen; + int uriLen; + int prefixLen; +} TAG_NAME; + +/* TAG represents an open element. + The name of the element is stored in both the document and API + encodings. The memory buffer 'buf' is a separately-allocated + memory area which stores the name. During the XML_Parse()/ + XMLParseBuffer() when the element is open, the memory for the 'raw' + version of the name (in the document encoding) is shared with the + document buffer. If the element is open across calls to + XML_Parse()/XML_ParseBuffer(), the buffer is re-allocated to + contain the 'raw' name as well. + + A parser re-uses these structures, maintaining a list of allocated + TAG objects in a free list. +*/ +typedef struct tag { + struct tag *parent; /* parent of this element */ + const char *rawName; /* tagName in the original encoding */ + int rawNameLength; + TAG_NAME name; /* tagName in the API encoding */ + char *buf; /* buffer for name components */ + char *bufEnd; /* end of the buffer */ + BINDING *bindings; +} TAG; + +typedef struct { + const XML_Char *name; + const XML_Char *textPtr; + int textLen; /* length in XML_Chars */ + int processed; /* # of processed bytes - when suspended */ + const XML_Char *systemId; + const XML_Char *base; + const XML_Char *publicId; + const XML_Char *notation; + XML_Bool open; + XML_Bool is_param; + XML_Bool is_internal; /* true if declared in internal subset outside PE */ +} ENTITY; + +typedef struct { + enum XML_Content_Type type; + enum XML_Content_Quant quant; + const XML_Char * name; + int firstchild; + int lastchild; + int childcnt; + int nextsib; +} CONTENT_SCAFFOLD; + +#define INIT_SCAFFOLD_ELEMENTS 32 + +typedef struct block { + struct block *next; + int size; + XML_Char s[1]; +} BLOCK; + +typedef struct { + BLOCK *blocks; + BLOCK *freeBlocks; + const XML_Char *end; + XML_Char *ptr; + XML_Char *start; + const XML_Memory_Handling_Suite *mem; +} STRING_POOL; + +/* The XML_Char before the name is used to determine whether + an attribute has been specified. */ +typedef struct attribute_id { + XML_Char *name; + PREFIX *prefix; + XML_Bool maybeTokenized; + XML_Bool xmlns; +} ATTRIBUTE_ID; + +typedef struct { + const ATTRIBUTE_ID *id; + XML_Bool isCdata; + const XML_Char *value; +} DEFAULT_ATTRIBUTE; + +typedef struct { + unsigned long version; + unsigned long hash; + const XML_Char *uriName; +} NS_ATT; + +typedef struct { + const XML_Char *name; + PREFIX *prefix; + const ATTRIBUTE_ID *idAtt; + int nDefaultAtts; + int allocDefaultAtts; + DEFAULT_ATTRIBUTE *defaultAtts; +} ELEMENT_TYPE; + +typedef struct { + HASH_TABLE generalEntities; + HASH_TABLE elementTypes; + HASH_TABLE attributeIds; + HASH_TABLE prefixes; + STRING_POOL pool; + STRING_POOL entityValuePool; + /* false once a parameter entity reference has been skipped */ + XML_Bool keepProcessing; + /* true once an internal or external PE reference has been encountered; + this includes the reference to an external subset */ + XML_Bool hasParamEntityRefs; + XML_Bool standalone; +#ifdef XML_DTD + /* indicates if external PE has been read */ + XML_Bool paramEntityRead; + HASH_TABLE paramEntities; +#endif /* XML_DTD */ + PREFIX defaultPrefix; + /* === scaffolding for building content model === */ + XML_Bool in_eldecl; + CONTENT_SCAFFOLD *scaffold; + unsigned contentStringLen; + unsigned scaffSize; + unsigned scaffCount; + int scaffLevel; + int *scaffIndex; +} DTD; + +typedef struct open_internal_entity { + const char *internalEventPtr; + const char *internalEventEndPtr; + struct open_internal_entity *next; + ENTITY *entity; + int startTagLevel; + XML_Bool betweenDecl; /* WFC: PE Between Declarations */ +} OPEN_INTERNAL_ENTITY; + +typedef enum XML_Error PTRCALL Processor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr); + +static Processor prologProcessor; +static Processor prologInitProcessor; +static Processor contentProcessor; +static Processor cdataSectionProcessor; +#ifdef XML_DTD +static Processor ignoreSectionProcessor; +static Processor externalParEntProcessor; +static Processor externalParEntInitProcessor; +static Processor entityValueProcessor; +static Processor entityValueInitProcessor; +#endif /* XML_DTD */ +static Processor epilogProcessor; +static Processor errorProcessor; +static Processor externalEntityInitProcessor; +static Processor externalEntityInitProcessor2; +static Processor externalEntityInitProcessor3; +static Processor externalEntityContentProcessor; +static Processor internalEntityProcessor; + +static enum XML_Error +handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName); +static enum XML_Error +processXmlDecl(XML_Parser parser, int isGeneralTextEntity, + const char *s, const char *next); +static enum XML_Error +initializeEncoding(XML_Parser parser); +static enum XML_Error +doProlog(XML_Parser parser, const ENCODING *enc, const char *s, + const char *end, int tok, const char *next, const char **nextPtr, + XML_Bool haveMore); +static enum XML_Error +processInternalEntity(XML_Parser parser, ENTITY *entity, + XML_Bool betweenDecl); +static enum XML_Error +doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, + const char *start, const char *end, const char **endPtr, + XML_Bool haveMore); +static enum XML_Error +doCdataSection(XML_Parser parser, const ENCODING *, const char **startPtr, + const char *end, const char **nextPtr, XML_Bool haveMore); +#ifdef XML_DTD +static enum XML_Error +doIgnoreSection(XML_Parser parser, const ENCODING *, const char **startPtr, + const char *end, const char **nextPtr, XML_Bool haveMore); +#endif /* XML_DTD */ + +static enum XML_Error +storeAtts(XML_Parser parser, const ENCODING *, const char *s, + TAG_NAME *tagNamePtr, BINDING **bindingsPtr); +static enum XML_Error +addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, + const XML_Char *uri, BINDING **bindingsPtr); +static int +defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *, XML_Bool isCdata, + XML_Bool isId, const XML_Char *dfltValue, XML_Parser parser); +static enum XML_Error +storeAttributeValue(XML_Parser parser, const ENCODING *, XML_Bool isCdata, + const char *, const char *, STRING_POOL *); +static enum XML_Error +appendAttributeValue(XML_Parser parser, const ENCODING *, XML_Bool isCdata, + const char *, const char *, STRING_POOL *); +static ATTRIBUTE_ID * +getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); +static int +setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *); +static enum XML_Error +storeEntityValue(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); +static int +reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end); +static int +reportComment(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); +static void +reportDefault(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); + +static const XML_Char * getContext(XML_Parser parser); +static XML_Bool +setContext(XML_Parser parser, const XML_Char *context); + +static void FASTCALL normalizePublicId(XML_Char *s); + +static DTD * dtdCreate(const XML_Memory_Handling_Suite *ms); +/* do not call if parentParser != NULL */ +static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms); +static void +dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms); +static int +dtdCopy(DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms); +static int +copyEntityTable(HASH_TABLE *, STRING_POOL *, const HASH_TABLE *); + +static NAMED * +lookup(HASH_TABLE *table, KEY name, size_t createSize); +static void FASTCALL +hashTableInit(HASH_TABLE *, const XML_Memory_Handling_Suite *ms); +static void FASTCALL hashTableClear(HASH_TABLE *); +static void FASTCALL hashTableDestroy(HASH_TABLE *); +static void FASTCALL +hashTableIterInit(HASH_TABLE_ITER *, const HASH_TABLE *); +static NAMED * FASTCALL hashTableIterNext(HASH_TABLE_ITER *); + +static void FASTCALL +poolInit(STRING_POOL *, const XML_Memory_Handling_Suite *ms); +static void FASTCALL poolClear(STRING_POOL *); +static void FASTCALL poolDestroy(STRING_POOL *); +static XML_Char * +poolAppend(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end); +static XML_Char * +poolStoreString(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end); +static XML_Bool FASTCALL poolGrow(STRING_POOL *pool); +static const XML_Char * FASTCALL +poolCopyString(STRING_POOL *pool, const XML_Char *s); +static const XML_Char * +poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n); +static const XML_Char * FASTCALL +poolAppendString(STRING_POOL *pool, const XML_Char *s); + +static int FASTCALL nextScaffoldPart(XML_Parser parser); +static XML_Content * build_model(XML_Parser parser); +static ELEMENT_TYPE * +getElementType(XML_Parser parser, const ENCODING *enc, + const char *ptr, const char *end); + +static XML_Parser +parserCreate(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *nameSep, + DTD *dtd); +static void +parserInit(XML_Parser parser, const XML_Char *encodingName); + +#define poolStart(pool) ((pool)->start) +#define poolEnd(pool) ((pool)->ptr) +#define poolLength(pool) ((pool)->ptr - (pool)->start) +#define poolChop(pool) ((void)--(pool->ptr)) +#define poolLastChar(pool) (((pool)->ptr)[-1]) +#define poolDiscard(pool) ((pool)->ptr = (pool)->start) +#define poolFinish(pool) ((pool)->start = (pool)->ptr) +#define poolAppendChar(pool, c) \ + (((pool)->ptr == (pool)->end && !poolGrow(pool)) \ + ? 0 \ + : ((*((pool)->ptr)++ = c), 1)) + +struct XML_ParserStruct { + /* The first member must be userData so that the XML_GetUserData + macro works. */ + void *m_userData; + void *m_handlerArg; + char *m_buffer; + const XML_Memory_Handling_Suite m_mem; + /* first character to be parsed */ + const char *m_bufferPtr; + /* past last character to be parsed */ + char *m_bufferEnd; + /* allocated end of buffer */ + const char *m_bufferLim; + XML_Index m_parseEndByteIndex; + const char *m_parseEndPtr; + XML_Char *m_dataBuf; + XML_Char *m_dataBufEnd; + XML_StartElementHandler m_startElementHandler; + XML_EndElementHandler m_endElementHandler; + XML_CharacterDataHandler m_characterDataHandler; + XML_ProcessingInstructionHandler m_processingInstructionHandler; + XML_CommentHandler m_commentHandler; + XML_StartCdataSectionHandler m_startCdataSectionHandler; + XML_EndCdataSectionHandler m_endCdataSectionHandler; + XML_DefaultHandler m_defaultHandler; + XML_StartDoctypeDeclHandler m_startDoctypeDeclHandler; + XML_EndDoctypeDeclHandler m_endDoctypeDeclHandler; + XML_UnparsedEntityDeclHandler m_unparsedEntityDeclHandler; + XML_NotationDeclHandler m_notationDeclHandler; + XML_StartNamespaceDeclHandler m_startNamespaceDeclHandler; + XML_EndNamespaceDeclHandler m_endNamespaceDeclHandler; + XML_NotStandaloneHandler m_notStandaloneHandler; + XML_ExternalEntityRefHandler m_externalEntityRefHandler; + XML_Parser m_externalEntityRefHandlerArg; + XML_SkippedEntityHandler m_skippedEntityHandler; + XML_UnknownEncodingHandler m_unknownEncodingHandler; + XML_ElementDeclHandler m_elementDeclHandler; + XML_AttlistDeclHandler m_attlistDeclHandler; + XML_EntityDeclHandler m_entityDeclHandler; + XML_XmlDeclHandler m_xmlDeclHandler; + const ENCODING *m_encoding; + INIT_ENCODING m_initEncoding; + const ENCODING *m_internalEncoding; + const XML_Char *m_protocolEncodingName; + XML_Bool m_ns; + XML_Bool m_ns_triplets; + void *m_unknownEncodingMem; + void *m_unknownEncodingData; + void *m_unknownEncodingHandlerData; + void (XMLCALL *m_unknownEncodingRelease)(void *); + PROLOG_STATE m_prologState; + Processor *m_processor; + enum XML_Error m_errorCode; + const char *m_eventPtr; + const char *m_eventEndPtr; + const char *m_positionPtr; + OPEN_INTERNAL_ENTITY *m_openInternalEntities; + OPEN_INTERNAL_ENTITY *m_freeInternalEntities; + XML_Bool m_defaultExpandInternalEntities; + int m_tagLevel; + ENTITY *m_declEntity; + const XML_Char *m_doctypeName; + const XML_Char *m_doctypeSysid; + const XML_Char *m_doctypePubid; + const XML_Char *m_declAttributeType; + const XML_Char *m_declNotationName; + const XML_Char *m_declNotationPublicId; + ELEMENT_TYPE *m_declElementType; + ATTRIBUTE_ID *m_declAttributeId; + XML_Bool m_declAttributeIsCdata; + XML_Bool m_declAttributeIsId; + DTD *m_dtd; + const XML_Char *m_curBase; + TAG *m_tagStack; + TAG *m_freeTagList; + BINDING *m_inheritedBindings; + BINDING *m_freeBindingList; + int m_attsSize; + int m_nSpecifiedAtts; + int m_idAttIndex; + ATTRIBUTE *m_atts; + NS_ATT *m_nsAtts; + unsigned long m_nsAttsVersion; + unsigned char m_nsAttsPower; + POSITION m_position; + STRING_POOL m_tempPool; + STRING_POOL m_temp2Pool; + char *m_groupConnector; + unsigned int m_groupSize; + XML_Char m_namespaceSeparator; + XML_Parser m_parentParser; + XML_ParsingStatus m_parsingStatus; +#ifdef XML_DTD + XML_Bool m_isParamEntity; + XML_Bool m_useForeignDTD; + enum XML_ParamEntityParsing m_paramEntityParsing; +#endif +}; + +#define MALLOC(s) (parser->m_mem.malloc_fcn((s))) +#define REALLOC(p,s) (parser->m_mem.realloc_fcn((p),(s))) +#define FREE(p) (parser->m_mem.free_fcn((p))) + +#define userData (parser->m_userData) +#define handlerArg (parser->m_handlerArg) +#define startElementHandler (parser->m_startElementHandler) +#define endElementHandler (parser->m_endElementHandler) +#define characterDataHandler (parser->m_characterDataHandler) +#define processingInstructionHandler \ + (parser->m_processingInstructionHandler) +#define commentHandler (parser->m_commentHandler) +#define startCdataSectionHandler \ + (parser->m_startCdataSectionHandler) +#define endCdataSectionHandler (parser->m_endCdataSectionHandler) +#define defaultHandler (parser->m_defaultHandler) +#define startDoctypeDeclHandler (parser->m_startDoctypeDeclHandler) +#define endDoctypeDeclHandler (parser->m_endDoctypeDeclHandler) +#define unparsedEntityDeclHandler \ + (parser->m_unparsedEntityDeclHandler) +#define notationDeclHandler (parser->m_notationDeclHandler) +#define startNamespaceDeclHandler \ + (parser->m_startNamespaceDeclHandler) +#define endNamespaceDeclHandler (parser->m_endNamespaceDeclHandler) +#define notStandaloneHandler (parser->m_notStandaloneHandler) +#define externalEntityRefHandler \ + (parser->m_externalEntityRefHandler) +#define externalEntityRefHandlerArg \ + (parser->m_externalEntityRefHandlerArg) +#define internalEntityRefHandler \ + (parser->m_internalEntityRefHandler) +#define skippedEntityHandler (parser->m_skippedEntityHandler) +#define unknownEncodingHandler (parser->m_unknownEncodingHandler) +#define elementDeclHandler (parser->m_elementDeclHandler) +#define attlistDeclHandler (parser->m_attlistDeclHandler) +#define entityDeclHandler (parser->m_entityDeclHandler) +#define xmlDeclHandler (parser->m_xmlDeclHandler) +#define encoding (parser->m_encoding) +#define initEncoding (parser->m_initEncoding) +#define internalEncoding (parser->m_internalEncoding) +#define unknownEncodingMem (parser->m_unknownEncodingMem) +#define unknownEncodingData (parser->m_unknownEncodingData) +#define unknownEncodingHandlerData \ + (parser->m_unknownEncodingHandlerData) +#define unknownEncodingRelease (parser->m_unknownEncodingRelease) +#define protocolEncodingName (parser->m_protocolEncodingName) +#define ns (parser->m_ns) +#define ns_triplets (parser->m_ns_triplets) +#define prologState (parser->m_prologState) +#define processor (parser->m_processor) +#define errorCode (parser->m_errorCode) +#define eventPtr (parser->m_eventPtr) +#define eventEndPtr (parser->m_eventEndPtr) +#define positionPtr (parser->m_positionPtr) +#define position (parser->m_position) +#define openInternalEntities (parser->m_openInternalEntities) +#define freeInternalEntities (parser->m_freeInternalEntities) +#define defaultExpandInternalEntities \ + (parser->m_defaultExpandInternalEntities) +#define tagLevel (parser->m_tagLevel) +#define buffer (parser->m_buffer) +#define bufferPtr (parser->m_bufferPtr) +#define bufferEnd (parser->m_bufferEnd) +#define parseEndByteIndex (parser->m_parseEndByteIndex) +#define parseEndPtr (parser->m_parseEndPtr) +#define bufferLim (parser->m_bufferLim) +#define dataBuf (parser->m_dataBuf) +#define dataBufEnd (parser->m_dataBufEnd) +#define _dtd (parser->m_dtd) +#define curBase (parser->m_curBase) +#define declEntity (parser->m_declEntity) +#define doctypeName (parser->m_doctypeName) +#define doctypeSysid (parser->m_doctypeSysid) +#define doctypePubid (parser->m_doctypePubid) +#define declAttributeType (parser->m_declAttributeType) +#define declNotationName (parser->m_declNotationName) +#define declNotationPublicId (parser->m_declNotationPublicId) +#define declElementType (parser->m_declElementType) +#define declAttributeId (parser->m_declAttributeId) +#define declAttributeIsCdata (parser->m_declAttributeIsCdata) +#define declAttributeIsId (parser->m_declAttributeIsId) +#define freeTagList (parser->m_freeTagList) +#define freeBindingList (parser->m_freeBindingList) +#define inheritedBindings (parser->m_inheritedBindings) +#define tagStack (parser->m_tagStack) +#define atts (parser->m_atts) +#define attsSize (parser->m_attsSize) +#define nSpecifiedAtts (parser->m_nSpecifiedAtts) +#define idAttIndex (parser->m_idAttIndex) +#define nsAtts (parser->m_nsAtts) +#define nsAttsVersion (parser->m_nsAttsVersion) +#define nsAttsPower (parser->m_nsAttsPower) +#define tempPool (parser->m_tempPool) +#define temp2Pool (parser->m_temp2Pool) +#define groupConnector (parser->m_groupConnector) +#define groupSize (parser->m_groupSize) +#define namespaceSeparator (parser->m_namespaceSeparator) +#define parentParser (parser->m_parentParser) +#define ps_parsing (parser->m_parsingStatus.parsing) +#define ps_finalBuffer (parser->m_parsingStatus.finalBuffer) +#ifdef XML_DTD +#define isParamEntity (parser->m_isParamEntity) +#define useForeignDTD (parser->m_useForeignDTD) +#define paramEntityParsing (parser->m_paramEntityParsing) +#endif /* XML_DTD */ + +XML_Parser XMLCALL +XML_ParserCreate(const XML_Char *encodingName) +{ + return XML_ParserCreate_MM(encodingName, NULL, NULL); +} + +XML_Parser XMLCALL +XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep) +{ + XML_Char tmp[2]; + *tmp = nsSep; + return XML_ParserCreate_MM(encodingName, NULL, tmp); +} + +static const XML_Char implicitContext[] = { + 'x', 'm', 'l', '=', 'h', 't', 't', 'p', ':', '/', '/', + 'w', 'w', 'w', '.', 'w', '3', '.', 'o', 'r', 'g', '/', + 'X', 'M', 'L', '/', '1', '9', '9', '8', '/', + 'n', 'a', 'm', 'e', 's', 'p', 'a', 'c', 'e', '\0' +}; + +XML_Parser XMLCALL +XML_ParserCreate_MM(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *nameSep) +{ + XML_Parser parser = parserCreate(encodingName, memsuite, nameSep, NULL); + if (parser != NULL && ns) { + /* implicit context only set for root parser, since child + parsers (i.e. external entity parsers) will inherit it + */ + if (!setContext(parser, implicitContext)) { + XML_ParserFree(parser); + return NULL; + } + } + return parser; +} + +static XML_Parser +parserCreate(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *nameSep, + DTD *dtd) +{ + XML_Parser parser; + + if (memsuite) { + XML_Memory_Handling_Suite *mtemp; + parser = (XML_Parser) + memsuite->malloc_fcn(sizeof(struct XML_ParserStruct)); + if (parser != NULL) { + mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); + mtemp->malloc_fcn = memsuite->malloc_fcn; + mtemp->realloc_fcn = memsuite->realloc_fcn; + mtemp->free_fcn = memsuite->free_fcn; + } + } + else { + XML_Memory_Handling_Suite *mtemp; + parser = (XML_Parser)malloc(sizeof(struct XML_ParserStruct)); + if (parser != NULL) { + mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); + mtemp->malloc_fcn = malloc; + mtemp->realloc_fcn = realloc; + mtemp->free_fcn = free; + } + } + + if (!parser) + return parser; + + buffer = NULL; + bufferLim = NULL; + + attsSize = INIT_ATTS_SIZE; + atts = (ATTRIBUTE *)MALLOC(attsSize * sizeof(ATTRIBUTE)); + if (atts == NULL) { + FREE(parser); + return NULL; + } + dataBuf = (XML_Char *)MALLOC(INIT_DATA_BUF_SIZE * sizeof(XML_Char)); + if (dataBuf == NULL) { + FREE(atts); + FREE(parser); + return NULL; + } + dataBufEnd = dataBuf + INIT_DATA_BUF_SIZE; + + if (dtd) + _dtd = dtd; + else { + _dtd = dtdCreate(&parser->m_mem); + if (_dtd == NULL) { + FREE(dataBuf); + FREE(atts); + FREE(parser); + return NULL; + } + } + + freeBindingList = NULL; + freeTagList = NULL; + freeInternalEntities = NULL; + + groupSize = 0; + groupConnector = NULL; + + unknownEncodingHandler = NULL; + unknownEncodingHandlerData = NULL; + + namespaceSeparator = '!'; + ns = XML_FALSE; + ns_triplets = XML_FALSE; + + nsAtts = NULL; + nsAttsVersion = 0; + nsAttsPower = 0; + + poolInit(&tempPool, &(parser->m_mem)); + poolInit(&temp2Pool, &(parser->m_mem)); + parserInit(parser, encodingName); + + if (encodingName && !protocolEncodingName) { + XML_ParserFree(parser); + return NULL; + } + + if (nameSep) { + ns = XML_TRUE; + internalEncoding = XmlGetInternalEncodingNS(); + namespaceSeparator = *nameSep; + } + else { + internalEncoding = XmlGetInternalEncoding(); + } + + return parser; +} + +static void +parserInit(XML_Parser parser, const XML_Char *encodingName) +{ + processor = prologInitProcessor; + XmlPrologStateInit(&prologState); + protocolEncodingName = (encodingName != NULL + ? poolCopyString(&tempPool, encodingName) + : NULL); + curBase = NULL; + XmlInitEncoding(&initEncoding, &encoding, 0); + userData = NULL; + handlerArg = NULL; + startElementHandler = NULL; + endElementHandler = NULL; + characterDataHandler = NULL; + processingInstructionHandler = NULL; + commentHandler = NULL; + startCdataSectionHandler = NULL; + endCdataSectionHandler = NULL; + defaultHandler = NULL; + startDoctypeDeclHandler = NULL; + endDoctypeDeclHandler = NULL; + unparsedEntityDeclHandler = NULL; + notationDeclHandler = NULL; + startNamespaceDeclHandler = NULL; + endNamespaceDeclHandler = NULL; + notStandaloneHandler = NULL; + externalEntityRefHandler = NULL; + externalEntityRefHandlerArg = parser; + skippedEntityHandler = NULL; + elementDeclHandler = NULL; + attlistDeclHandler = NULL; + entityDeclHandler = NULL; + xmlDeclHandler = NULL; + bufferPtr = buffer; + bufferEnd = buffer; + parseEndByteIndex = 0; + parseEndPtr = NULL; + declElementType = NULL; + declAttributeId = NULL; + declEntity = NULL; + doctypeName = NULL; + doctypeSysid = NULL; + doctypePubid = NULL; + declAttributeType = NULL; + declNotationName = NULL; + declNotationPublicId = NULL; + declAttributeIsCdata = XML_FALSE; + declAttributeIsId = XML_FALSE; + memset(&position, 0, sizeof(POSITION)); + errorCode = XML_ERROR_NONE; + eventPtr = NULL; + eventEndPtr = NULL; + positionPtr = NULL; + openInternalEntities = NULL; + defaultExpandInternalEntities = XML_TRUE; + tagLevel = 0; + tagStack = NULL; + inheritedBindings = NULL; + nSpecifiedAtts = 0; + unknownEncodingMem = NULL; + unknownEncodingRelease = NULL; + unknownEncodingData = NULL; + parentParser = NULL; + ps_parsing = XML_INITIALIZED; +#ifdef XML_DTD + isParamEntity = XML_FALSE; + useForeignDTD = XML_FALSE; + paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; +#endif +} + +/* moves list of bindings to freeBindingList */ +static void FASTCALL +moveToFreeBindingList(XML_Parser parser, BINDING *bindings) +{ + while (bindings) { + BINDING *b = bindings; + bindings = bindings->nextTagBinding; + b->nextTagBinding = freeBindingList; + freeBindingList = b; + } +} + +XML_Bool XMLCALL +XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) +{ + TAG *tStk; + OPEN_INTERNAL_ENTITY *openEntityList; + if (parentParser) + return XML_FALSE; + /* move tagStack to freeTagList */ + tStk = tagStack; + while (tStk) { + TAG *tag = tStk; + tStk = tStk->parent; + tag->parent = freeTagList; + moveToFreeBindingList(parser, tag->bindings); + tag->bindings = NULL; + freeTagList = tag; + } + /* move openInternalEntities to freeInternalEntities */ + openEntityList = openInternalEntities; + while (openEntityList) { + OPEN_INTERNAL_ENTITY *openEntity = openEntityList; + openEntityList = openEntity->next; + openEntity->next = freeInternalEntities; + freeInternalEntities = openEntity; + } + moveToFreeBindingList(parser, inheritedBindings); + FREE(unknownEncodingMem); + if (unknownEncodingRelease) + unknownEncodingRelease(unknownEncodingData); + poolClear(&tempPool); + poolClear(&temp2Pool); + parserInit(parser, encodingName); + dtdReset(_dtd, &parser->m_mem); + return setContext(parser, implicitContext); +} + +enum XML_Status XMLCALL +XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) +{ + /* Block after XML_Parse()/XML_ParseBuffer() has been called. + XXX There's no way for the caller to determine which of the + XXX possible error cases caused the XML_STATUS_ERROR return. + */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return XML_STATUS_ERROR; + if (encodingName == NULL) + protocolEncodingName = NULL; + else { + protocolEncodingName = poolCopyString(&tempPool, encodingName); + if (!protocolEncodingName) + return XML_STATUS_ERROR; + } + return XML_STATUS_OK; +} + +XML_Parser XMLCALL +XML_ExternalEntityParserCreate(XML_Parser oldParser, + const XML_Char *context, + const XML_Char *encodingName) +{ + XML_Parser parser = oldParser; + DTD *newDtd = NULL; + DTD *oldDtd = _dtd; + XML_StartElementHandler oldStartElementHandler = startElementHandler; + XML_EndElementHandler oldEndElementHandler = endElementHandler; + XML_CharacterDataHandler oldCharacterDataHandler = characterDataHandler; + XML_ProcessingInstructionHandler oldProcessingInstructionHandler + = processingInstructionHandler; + XML_CommentHandler oldCommentHandler = commentHandler; + XML_StartCdataSectionHandler oldStartCdataSectionHandler + = startCdataSectionHandler; + XML_EndCdataSectionHandler oldEndCdataSectionHandler + = endCdataSectionHandler; + XML_DefaultHandler oldDefaultHandler = defaultHandler; + XML_UnparsedEntityDeclHandler oldUnparsedEntityDeclHandler + = unparsedEntityDeclHandler; + XML_NotationDeclHandler oldNotationDeclHandler = notationDeclHandler; + XML_StartNamespaceDeclHandler oldStartNamespaceDeclHandler + = startNamespaceDeclHandler; + XML_EndNamespaceDeclHandler oldEndNamespaceDeclHandler + = endNamespaceDeclHandler; + XML_NotStandaloneHandler oldNotStandaloneHandler = notStandaloneHandler; + XML_ExternalEntityRefHandler oldExternalEntityRefHandler + = externalEntityRefHandler; + XML_SkippedEntityHandler oldSkippedEntityHandler = skippedEntityHandler; + XML_UnknownEncodingHandler oldUnknownEncodingHandler + = unknownEncodingHandler; + XML_ElementDeclHandler oldElementDeclHandler = elementDeclHandler; + XML_AttlistDeclHandler oldAttlistDeclHandler = attlistDeclHandler; + XML_EntityDeclHandler oldEntityDeclHandler = entityDeclHandler; + XML_XmlDeclHandler oldXmlDeclHandler = xmlDeclHandler; + ELEMENT_TYPE * oldDeclElementType = declElementType; + + void *oldUserData = userData; + void *oldHandlerArg = handlerArg; + XML_Bool oldDefaultExpandInternalEntities = defaultExpandInternalEntities; + XML_Parser oldExternalEntityRefHandlerArg = externalEntityRefHandlerArg; +#ifdef XML_DTD + enum XML_ParamEntityParsing oldParamEntityParsing = paramEntityParsing; + int oldInEntityValue = prologState.inEntityValue; +#endif + XML_Bool oldns_triplets = ns_triplets; + +#ifdef XML_DTD + if (!context) + newDtd = oldDtd; +#endif /* XML_DTD */ + + /* Note that the magical uses of the pre-processor to make field + access look more like C++ require that `parser' be overwritten + here. This makes this function more painful to follow than it + would be otherwise. + */ + if (ns) { + XML_Char tmp[2]; + *tmp = namespaceSeparator; + parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd); + } + else { + parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd); + } + + if (!parser) + return NULL; + + startElementHandler = oldStartElementHandler; + endElementHandler = oldEndElementHandler; + characterDataHandler = oldCharacterDataHandler; + processingInstructionHandler = oldProcessingInstructionHandler; + commentHandler = oldCommentHandler; + startCdataSectionHandler = oldStartCdataSectionHandler; + endCdataSectionHandler = oldEndCdataSectionHandler; + defaultHandler = oldDefaultHandler; + unparsedEntityDeclHandler = oldUnparsedEntityDeclHandler; + notationDeclHandler = oldNotationDeclHandler; + startNamespaceDeclHandler = oldStartNamespaceDeclHandler; + endNamespaceDeclHandler = oldEndNamespaceDeclHandler; + notStandaloneHandler = oldNotStandaloneHandler; + externalEntityRefHandler = oldExternalEntityRefHandler; + skippedEntityHandler = oldSkippedEntityHandler; + unknownEncodingHandler = oldUnknownEncodingHandler; + elementDeclHandler = oldElementDeclHandler; + attlistDeclHandler = oldAttlistDeclHandler; + entityDeclHandler = oldEntityDeclHandler; + xmlDeclHandler = oldXmlDeclHandler; + declElementType = oldDeclElementType; + userData = oldUserData; + if (oldUserData == oldHandlerArg) + handlerArg = userData; + else + handlerArg = parser; + if (oldExternalEntityRefHandlerArg != oldParser) + externalEntityRefHandlerArg = oldExternalEntityRefHandlerArg; + defaultExpandInternalEntities = oldDefaultExpandInternalEntities; + ns_triplets = oldns_triplets; + parentParser = oldParser; +#ifdef XML_DTD + paramEntityParsing = oldParamEntityParsing; + prologState.inEntityValue = oldInEntityValue; + if (context) { +#endif /* XML_DTD */ + if (!dtdCopy(_dtd, oldDtd, &parser->m_mem) + || !setContext(parser, context)) { + XML_ParserFree(parser); + return NULL; + } + processor = externalEntityInitProcessor; +#ifdef XML_DTD + } + else { + /* The DTD instance referenced by _dtd is shared between the document's + root parser and external PE parsers, therefore one does not need to + call setContext. In addition, one also *must* not call setContext, + because this would overwrite existing prefix->binding pointers in + _dtd with ones that get destroyed with the external PE parser. + This would leave those prefixes with dangling pointers. + */ + isParamEntity = XML_TRUE; + XmlPrologStateInitExternalEntity(&prologState); + processor = externalParEntInitProcessor; + } +#endif /* XML_DTD */ + return parser; +} + +static void FASTCALL +destroyBindings(BINDING *bindings, XML_Parser parser) +{ + for (;;) { + BINDING *b = bindings; + if (!b) + break; + bindings = b->nextTagBinding; + FREE(b->uri); + FREE(b); + } +} + +void XMLCALL +XML_ParserFree(XML_Parser parser) +{ + TAG *tagList; + OPEN_INTERNAL_ENTITY *entityList; + if (parser == NULL) + return; + /* free tagStack and freeTagList */ + tagList = tagStack; + for (;;) { + TAG *p; + if (tagList == NULL) { + if (freeTagList == NULL) + break; + tagList = freeTagList; + freeTagList = NULL; + } + p = tagList; + tagList = tagList->parent; + FREE(p->buf); + destroyBindings(p->bindings, parser); + FREE(p); + } + /* free openInternalEntities and freeInternalEntities */ + entityList = openInternalEntities; + for (;;) { + OPEN_INTERNAL_ENTITY *openEntity; + if (entityList == NULL) { + if (freeInternalEntities == NULL) + break; + entityList = freeInternalEntities; + freeInternalEntities = NULL; + } + openEntity = entityList; + entityList = entityList->next; + FREE(openEntity); + } + + destroyBindings(freeBindingList, parser); + destroyBindings(inheritedBindings, parser); + poolDestroy(&tempPool); + poolDestroy(&temp2Pool); +#ifdef XML_DTD + /* external parameter entity parsers share the DTD structure + parser->m_dtd with the root parser, so we must not destroy it + */ + if (!isParamEntity && _dtd) +#else + if (_dtd) +#endif /* XML_DTD */ + dtdDestroy(_dtd, (XML_Bool)!parentParser, &parser->m_mem); + FREE((void *)atts); + FREE(groupConnector); + FREE(buffer); + FREE(dataBuf); + FREE(nsAtts); + FREE(unknownEncodingMem); + if (unknownEncodingRelease) + unknownEncodingRelease(unknownEncodingData); + FREE(parser); +} + +void XMLCALL +XML_UseParserAsHandlerArg(XML_Parser parser) +{ + handlerArg = parser; +} + +enum XML_Error XMLCALL +XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD) +{ +#ifdef XML_DTD + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING; + useForeignDTD = useDTD; + return XML_ERROR_NONE; +#else + return XML_ERROR_FEATURE_REQUIRES_XML_DTD; +#endif +} + +void XMLCALL +XML_SetReturnNSTriplet(XML_Parser parser, int do_nst) +{ + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return; + ns_triplets = do_nst ? XML_TRUE : XML_FALSE; +} + +void XMLCALL +XML_SetUserData(XML_Parser parser, void *p) +{ + if (handlerArg == userData) + handlerArg = userData = p; + else + userData = p; +} + +enum XML_Status XMLCALL +XML_SetBase(XML_Parser parser, const XML_Char *p) +{ + if (p) { + p = poolCopyString(&_dtd->pool, p); + if (!p) + return XML_STATUS_ERROR; + curBase = p; + } + else + curBase = NULL; + return XML_STATUS_OK; +} + +const XML_Char * XMLCALL +XML_GetBase(XML_Parser parser) +{ + return curBase; +} + +int XMLCALL +XML_GetSpecifiedAttributeCount(XML_Parser parser) +{ + return nSpecifiedAtts; +} + +int XMLCALL +XML_GetIdAttributeIndex(XML_Parser parser) +{ + return idAttIndex; +} + +void XMLCALL +XML_SetElementHandler(XML_Parser parser, + XML_StartElementHandler start, + XML_EndElementHandler end) +{ + startElementHandler = start; + endElementHandler = end; +} + +void XMLCALL +XML_SetStartElementHandler(XML_Parser parser, + XML_StartElementHandler start) { + startElementHandler = start; +} + +void XMLCALL +XML_SetEndElementHandler(XML_Parser parser, + XML_EndElementHandler end) { + endElementHandler = end; +} + +void XMLCALL +XML_SetCharacterDataHandler(XML_Parser parser, + XML_CharacterDataHandler handler) +{ + characterDataHandler = handler; +} + +void XMLCALL +XML_SetProcessingInstructionHandler(XML_Parser parser, + XML_ProcessingInstructionHandler handler) +{ + processingInstructionHandler = handler; +} + +void XMLCALL +XML_SetCommentHandler(XML_Parser parser, + XML_CommentHandler handler) +{ + commentHandler = handler; +} + +void XMLCALL +XML_SetCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start, + XML_EndCdataSectionHandler end) +{ + startCdataSectionHandler = start; + endCdataSectionHandler = end; +} + +void XMLCALL +XML_SetStartCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start) { + startCdataSectionHandler = start; +} + +void XMLCALL +XML_SetEndCdataSectionHandler(XML_Parser parser, + XML_EndCdataSectionHandler end) { + endCdataSectionHandler = end; +} + +void XMLCALL +XML_SetDefaultHandler(XML_Parser parser, + XML_DefaultHandler handler) +{ + defaultHandler = handler; + defaultExpandInternalEntities = XML_FALSE; +} + +void XMLCALL +XML_SetDefaultHandlerExpand(XML_Parser parser, + XML_DefaultHandler handler) +{ + defaultHandler = handler; + defaultExpandInternalEntities = XML_TRUE; +} + +void XMLCALL +XML_SetDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start, + XML_EndDoctypeDeclHandler end) +{ + startDoctypeDeclHandler = start; + endDoctypeDeclHandler = end; +} + +void XMLCALL +XML_SetStartDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start) { + startDoctypeDeclHandler = start; +} + +void XMLCALL +XML_SetEndDoctypeDeclHandler(XML_Parser parser, + XML_EndDoctypeDeclHandler end) { + endDoctypeDeclHandler = end; +} + +void XMLCALL +XML_SetUnparsedEntityDeclHandler(XML_Parser parser, + XML_UnparsedEntityDeclHandler handler) +{ + unparsedEntityDeclHandler = handler; +} + +void XMLCALL +XML_SetNotationDeclHandler(XML_Parser parser, + XML_NotationDeclHandler handler) +{ + notationDeclHandler = handler; +} + +void XMLCALL +XML_SetNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start, + XML_EndNamespaceDeclHandler end) +{ + startNamespaceDeclHandler = start; + endNamespaceDeclHandler = end; +} + +void XMLCALL +XML_SetStartNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start) { + startNamespaceDeclHandler = start; +} + +void XMLCALL +XML_SetEndNamespaceDeclHandler(XML_Parser parser, + XML_EndNamespaceDeclHandler end) { + endNamespaceDeclHandler = end; +} + +void XMLCALL +XML_SetNotStandaloneHandler(XML_Parser parser, + XML_NotStandaloneHandler handler) +{ + notStandaloneHandler = handler; +} + +void XMLCALL +XML_SetExternalEntityRefHandler(XML_Parser parser, + XML_ExternalEntityRefHandler handler) +{ + externalEntityRefHandler = handler; +} + +void XMLCALL +XML_SetExternalEntityRefHandlerArg(XML_Parser parser, void *arg) +{ + if (arg) + externalEntityRefHandlerArg = (XML_Parser)arg; + else + externalEntityRefHandlerArg = parser; +} + +void XMLCALL +XML_SetSkippedEntityHandler(XML_Parser parser, + XML_SkippedEntityHandler handler) +{ + skippedEntityHandler = handler; +} + +void XMLCALL +XML_SetUnknownEncodingHandler(XML_Parser parser, + XML_UnknownEncodingHandler handler, + void *data) +{ + unknownEncodingHandler = handler; + unknownEncodingHandlerData = data; +} + +void XMLCALL +XML_SetElementDeclHandler(XML_Parser parser, + XML_ElementDeclHandler eldecl) +{ + elementDeclHandler = eldecl; +} + +void XMLCALL +XML_SetAttlistDeclHandler(XML_Parser parser, + XML_AttlistDeclHandler attdecl) +{ + attlistDeclHandler = attdecl; +} + +void XMLCALL +XML_SetEntityDeclHandler(XML_Parser parser, + XML_EntityDeclHandler handler) +{ + entityDeclHandler = handler; +} + +void XMLCALL +XML_SetXmlDeclHandler(XML_Parser parser, + XML_XmlDeclHandler handler) { + xmlDeclHandler = handler; +} + +int XMLCALL +XML_SetParamEntityParsing(XML_Parser parser, + enum XML_ParamEntityParsing peParsing) +{ + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return 0; +#ifdef XML_DTD + paramEntityParsing = peParsing; + return 1; +#else + return peParsing == XML_PARAM_ENTITY_PARSING_NEVER; +#endif +} + +enum XML_Status XMLCALL +XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) +{ + switch (ps_parsing) { + case XML_SUSPENDED: + errorCode = XML_ERROR_SUSPENDED; + return XML_STATUS_ERROR; + case XML_FINISHED: + errorCode = XML_ERROR_FINISHED; + return XML_STATUS_ERROR; + default: + ps_parsing = XML_PARSING; + } + + if (len == 0) { + ps_finalBuffer = (XML_Bool)isFinal; + if (!isFinal) + return XML_STATUS_OK; + positionPtr = bufferPtr; + parseEndPtr = bufferEnd; + + /* If data are left over from last buffer, and we now know that these + data are the final chunk of input, then we have to check them again + to detect errors based on that fact. + */ + errorCode = processor(parser, bufferPtr, parseEndPtr, &bufferPtr); + + if (errorCode == XML_ERROR_NONE) { + switch (ps_parsing) { + case XML_SUSPENDED: + XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); + positionPtr = bufferPtr; + return XML_STATUS_SUSPENDED; + case XML_INITIALIZED: + case XML_PARSING: + ps_parsing = XML_FINISHED; + /* fall through */ + default: + return XML_STATUS_OK; + } + } + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } +#ifndef XML_CONTEXT_BYTES + else if (bufferPtr == bufferEnd) { + const char *end; + int nLeftOver; + enum XML_Error result; + parseEndByteIndex += len; + positionPtr = s; + ps_finalBuffer = (XML_Bool)isFinal; + + errorCode = processor(parser, s, parseEndPtr = s + len, &end); + + if (errorCode != XML_ERROR_NONE) { + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } + else { + switch (ps_parsing) { + case XML_SUSPENDED: + result = XML_STATUS_SUSPENDED; + break; + case XML_INITIALIZED: + case XML_PARSING: + result = XML_STATUS_OK; + if (isFinal) { + ps_parsing = XML_FINISHED; + return result; + } + } + } + + XmlUpdatePosition(encoding, positionPtr, end, &position); + nLeftOver = s + len - end; + if (nLeftOver) { + if (buffer == NULL || nLeftOver > bufferLim - buffer) { + /* FIXME avoid integer overflow */ + char *temp; + temp = (buffer == NULL + ? (char *)MALLOC(len * 2) + : (char *)REALLOC(buffer, len * 2)); + if (temp == NULL) { + errorCode = XML_ERROR_NO_MEMORY; + return XML_STATUS_ERROR; + } + buffer = temp; + if (!buffer) { + errorCode = XML_ERROR_NO_MEMORY; + eventPtr = eventEndPtr = NULL; + processor = errorProcessor; + return XML_STATUS_ERROR; + } + bufferLim = buffer + len * 2; + } + memcpy(buffer, end, nLeftOver); + } + bufferPtr = buffer; + bufferEnd = buffer + nLeftOver; + positionPtr = bufferPtr; + parseEndPtr = bufferEnd; + eventPtr = bufferPtr; + eventEndPtr = bufferPtr; + return result; + } +#endif /* not defined XML_CONTEXT_BYTES */ + else { + void *buff = XML_GetBuffer(parser, len); + if (buff == NULL) + return XML_STATUS_ERROR; + else { + memcpy(buff, s, len); + return XML_ParseBuffer(parser, len, isFinal); + } + } +} + +enum XML_Status XMLCALL +XML_ParseBuffer(XML_Parser parser, int len, int isFinal) +{ + const char *start; + enum XML_Status result = XML_STATUS_OK; + + switch (ps_parsing) { + case XML_SUSPENDED: + errorCode = XML_ERROR_SUSPENDED; + return XML_STATUS_ERROR; + case XML_FINISHED: + errorCode = XML_ERROR_FINISHED; + return XML_STATUS_ERROR; + default: + ps_parsing = XML_PARSING; + } + + start = bufferPtr; + positionPtr = start; + bufferEnd += len; + parseEndPtr = bufferEnd; + parseEndByteIndex += len; + ps_finalBuffer = (XML_Bool)isFinal; + + errorCode = processor(parser, start, parseEndPtr, &bufferPtr); + + if (errorCode != XML_ERROR_NONE) { + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } + else { + switch (ps_parsing) { + case XML_SUSPENDED: + result = XML_STATUS_SUSPENDED; + break; + case XML_INITIALIZED: + case XML_PARSING: + if (isFinal) { + ps_parsing = XML_FINISHED; + return result; + } + default: ; /* should not happen */ + } + } + + XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); + positionPtr = bufferPtr; + return result; +} + +void * XMLCALL +XML_GetBuffer(XML_Parser parser, int len) +{ + switch (ps_parsing) { + case XML_SUSPENDED: + errorCode = XML_ERROR_SUSPENDED; + return NULL; + case XML_FINISHED: + errorCode = XML_ERROR_FINISHED; + return NULL; + default: ; + } + + if (len > bufferLim - bufferEnd) { + /* FIXME avoid integer overflow */ + int neededSize = len + (int)(bufferEnd - bufferPtr); +#ifdef XML_CONTEXT_BYTES + int keep = (int)(bufferPtr - buffer); + + if (keep > XML_CONTEXT_BYTES) + keep = XML_CONTEXT_BYTES; + neededSize += keep; +#endif /* defined XML_CONTEXT_BYTES */ + if (neededSize <= bufferLim - buffer) { +#ifdef XML_CONTEXT_BYTES + if (keep < bufferPtr - buffer) { + int offset = (int)(bufferPtr - buffer) - keep; + memmove(buffer, &buffer[offset], bufferEnd - bufferPtr + keep); + bufferEnd -= offset; + bufferPtr -= offset; + } +#else + memmove(buffer, bufferPtr, bufferEnd - bufferPtr); + bufferEnd = buffer + (bufferEnd - bufferPtr); + bufferPtr = buffer; +#endif /* not defined XML_CONTEXT_BYTES */ + } + else { + char *newBuf; + int bufferSize = (int)(bufferLim - bufferPtr); + if (bufferSize == 0) + bufferSize = INIT_BUFFER_SIZE; + do { + bufferSize *= 2; + } while (bufferSize < neededSize); + newBuf = (char *)MALLOC(bufferSize); + if (newBuf == 0) { + errorCode = XML_ERROR_NO_MEMORY; + return NULL; + } + bufferLim = newBuf + bufferSize; +#ifdef XML_CONTEXT_BYTES + if (bufferPtr) { + int keep = (int)(bufferPtr - buffer); + if (keep > XML_CONTEXT_BYTES) + keep = XML_CONTEXT_BYTES; + memcpy(newBuf, &bufferPtr[-keep], bufferEnd - bufferPtr + keep); + FREE(buffer); + buffer = newBuf; + bufferEnd = buffer + (bufferEnd - bufferPtr) + keep; + bufferPtr = buffer + keep; + } + else { + bufferEnd = newBuf + (bufferEnd - bufferPtr); + bufferPtr = buffer = newBuf; + } +#else + if (bufferPtr) { + memcpy(newBuf, bufferPtr, bufferEnd - bufferPtr); + FREE(buffer); + } + bufferEnd = newBuf + (bufferEnd - bufferPtr); + bufferPtr = buffer = newBuf; +#endif /* not defined XML_CONTEXT_BYTES */ + } + } + return bufferEnd; +} + +enum XML_Status XMLCALL +XML_StopParser(XML_Parser parser, XML_Bool resumable) +{ + switch (ps_parsing) { + case XML_SUSPENDED: + if (resumable) { + errorCode = XML_ERROR_SUSPENDED; + return XML_STATUS_ERROR; + } + ps_parsing = XML_FINISHED; + break; + case XML_FINISHED: + errorCode = XML_ERROR_FINISHED; + return XML_STATUS_ERROR; + default: + if (resumable) { +#ifdef XML_DTD + if (isParamEntity) { + errorCode = XML_ERROR_SUSPEND_PE; + return XML_STATUS_ERROR; + } +#endif + ps_parsing = XML_SUSPENDED; + } + else + ps_parsing = XML_FINISHED; + } + return XML_STATUS_OK; +} + +enum XML_Status XMLCALL +XML_ResumeParser(XML_Parser parser) +{ + enum XML_Status result = XML_STATUS_OK; + + if (ps_parsing != XML_SUSPENDED) { + errorCode = XML_ERROR_NOT_SUSPENDED; + return XML_STATUS_ERROR; + } + ps_parsing = XML_PARSING; + + errorCode = processor(parser, bufferPtr, parseEndPtr, &bufferPtr); + + if (errorCode != XML_ERROR_NONE) { + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } + else { + switch (ps_parsing) { + case XML_SUSPENDED: + result = XML_STATUS_SUSPENDED; + break; + case XML_INITIALIZED: + case XML_PARSING: + if (ps_finalBuffer) { + ps_parsing = XML_FINISHED; + return result; + } + default: ; + } + } + + XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); + positionPtr = bufferPtr; + return result; +} + +void XMLCALL +XML_GetParsingStatus(XML_Parser parser, XML_ParsingStatus *status) +{ + assert(status != NULL); + *status = parser->m_parsingStatus; +} + +enum XML_Error XMLCALL +XML_GetErrorCode(XML_Parser parser) +{ + return errorCode; +} + +XML_Index XMLCALL +XML_GetCurrentByteIndex(XML_Parser parser) +{ + if (eventPtr) + return parseEndByteIndex - (parseEndPtr - eventPtr); + return -1; +} + +int XMLCALL +XML_GetCurrentByteCount(XML_Parser parser) +{ + if (eventEndPtr && eventPtr) + return (int)(eventEndPtr - eventPtr); + return 0; +} + +const char * XMLCALL +XML_GetInputContext(XML_Parser parser, int *offset, int *size) +{ +#ifdef XML_CONTEXT_BYTES + if (eventPtr && buffer) { + *offset = (int)(eventPtr - buffer); + *size = (int)(bufferEnd - buffer); + return buffer; + } +#endif /* defined XML_CONTEXT_BYTES */ + return (char *) 0; +} + +XML_Size XMLCALL +XML_GetCurrentLineNumber(XML_Parser parser) +{ + if (eventPtr && eventPtr >= positionPtr) { + XmlUpdatePosition(encoding, positionPtr, eventPtr, &position); + positionPtr = eventPtr; + } + return position.lineNumber + 1; +} + +XML_Size XMLCALL +XML_GetCurrentColumnNumber(XML_Parser parser) +{ + if (eventPtr && eventPtr >= positionPtr) { + XmlUpdatePosition(encoding, positionPtr, eventPtr, &position); + positionPtr = eventPtr; + } + return position.columnNumber; +} + +void XMLCALL +XML_FreeContentModel(XML_Parser parser, XML_Content *model) +{ + FREE(model); +} + +void * XMLCALL +XML_MemMalloc(XML_Parser parser, size_t size) +{ + return MALLOC(size); +} + +void * XMLCALL +XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) +{ + return REALLOC(ptr, size); +} + +void XMLCALL +XML_MemFree(XML_Parser parser, void *ptr) +{ + FREE(ptr); +} + +void XMLCALL +XML_DefaultCurrent(XML_Parser parser) +{ + if (defaultHandler) { + if (openInternalEntities) + reportDefault(parser, + internalEncoding, + openInternalEntities->internalEventPtr, + openInternalEntities->internalEventEndPtr); + else + reportDefault(parser, encoding, eventPtr, eventEndPtr); + } +} + +const XML_LChar * XMLCALL +XML_ErrorString(enum XML_Error code) +{ + static const XML_LChar* const message[] = { + 0, + XML_L("out of memory"), + XML_L("syntax error"), + XML_L("no element found"), + XML_L("not well-formed (invalid token)"), + XML_L("unclosed token"), + XML_L("partial character"), + XML_L("mismatched tag"), + XML_L("duplicate attribute"), + XML_L("junk after document element"), + XML_L("illegal parameter entity reference"), + XML_L("undefined entity"), + XML_L("recursive entity reference"), + XML_L("asynchronous entity"), + XML_L("reference to invalid character number"), + XML_L("reference to binary entity"), + XML_L("reference to external entity in attribute"), + XML_L("XML or text declaration not at start of entity"), + XML_L("unknown encoding"), + XML_L("encoding specified in XML declaration is incorrect"), + XML_L("unclosed CDATA section"), + XML_L("error in processing external entity reference"), + XML_L("document is not standalone"), + XML_L("unexpected parser state - please send a bug report"), + XML_L("entity declared in parameter entity"), + XML_L("requested feature requires XML_DTD support in Expat"), + XML_L("cannot change setting once parsing has begun"), + XML_L("unbound prefix"), + XML_L("must not undeclare prefix"), + XML_L("incomplete markup in parameter entity"), + XML_L("XML declaration not well-formed"), + XML_L("text declaration not well-formed"), + XML_L("illegal character(s) in public id"), + XML_L("parser suspended"), + XML_L("parser not suspended"), + XML_L("parsing aborted"), + XML_L("parsing finished"), + XML_L("cannot suspend in external parameter entity"), + XML_L("reserved prefix (xml) must not be undeclared or bound to another namespace name"), + XML_L("reserved prefix (xmlns) must not be declared or undeclared"), + XML_L("prefix must not be bound to one of the reserved namespace names") + }; + if (code > 0 && code < sizeof(message)/sizeof(message[0])) + return message[code]; + return NULL; +} + +const XML_LChar * XMLCALL +XML_ExpatVersion(void) { + + /* V1 is used to string-ize the version number. However, it would + string-ize the actual version macro *names* unless we get them + substituted before being passed to V1. CPP is defined to expand + a macro, then rescan for more expansions. Thus, we use V2 to expand + the version macros, then CPP will expand the resulting V1() macro + with the correct numerals. */ + /* ### I'm assuming cpp is portable in this respect... */ + +#define V1(a,b,c) XML_L(#a)XML_L(".")XML_L(#b)XML_L(".")XML_L(#c) +#define V2(a,b,c) XML_L("expat_")V1(a,b,c) + + return V2(XML_MAJOR_VERSION, XML_MINOR_VERSION, XML_MICRO_VERSION); + +#undef V1 +#undef V2 +} + +XML_Expat_Version XMLCALL +XML_ExpatVersionInfo(void) +{ + XML_Expat_Version version; + + version.major = XML_MAJOR_VERSION; + version.minor = XML_MINOR_VERSION; + version.micro = XML_MICRO_VERSION; + + return version; +} + +const XML_Feature * XMLCALL +XML_GetFeatureList(void) +{ + static const XML_Feature features[] = { + {XML_FEATURE_SIZEOF_XML_CHAR, XML_L("sizeof(XML_Char)"), + sizeof(XML_Char)}, + {XML_FEATURE_SIZEOF_XML_LCHAR, XML_L("sizeof(XML_LChar)"), + sizeof(XML_LChar)}, +#ifdef XML_UNICODE + {XML_FEATURE_UNICODE, XML_L("XML_UNICODE"), 0}, +#endif +#ifdef XML_UNICODE_WCHAR_T + {XML_FEATURE_UNICODE_WCHAR_T, XML_L("XML_UNICODE_WCHAR_T"), 0}, +#endif +#ifdef XML_DTD + {XML_FEATURE_DTD, XML_L("XML_DTD"), 0}, +#endif +#ifdef XML_CONTEXT_BYTES + {XML_FEATURE_CONTEXT_BYTES, XML_L("XML_CONTEXT_BYTES"), + XML_CONTEXT_BYTES}, +#endif +#ifdef XML_MIN_SIZE + {XML_FEATURE_MIN_SIZE, XML_L("XML_MIN_SIZE"), 0}, +#endif +#ifdef XML_NS + {XML_FEATURE_NS, XML_L("XML_NS"), 0}, +#endif + {XML_FEATURE_END, NULL, 0} + }; + + return features; +} + +/* Initially tag->rawName always points into the parse buffer; + for those TAG instances opened while the current parse buffer was + processed, and not yet closed, we need to store tag->rawName in a more + permanent location, since the parse buffer is about to be discarded. +*/ +static XML_Bool +storeRawNames(XML_Parser parser) +{ + TAG *tag = tagStack; + while (tag) { + int bufSize; + int nameLen = sizeof(XML_Char) * (tag->name.strLen + 1); + char *rawNameBuf = tag->buf + nameLen; + /* Stop if already stored. Since tagStack is a stack, we can stop + at the first entry that has already been copied; everything + below it in the stack is already been accounted for in a + previous call to this function. + */ + if (tag->rawName == rawNameBuf) + break; + /* For re-use purposes we need to ensure that the + size of tag->buf is a multiple of sizeof(XML_Char). + */ + bufSize = nameLen + ROUND_UP(tag->rawNameLength, sizeof(XML_Char)); + if (bufSize > tag->bufEnd - tag->buf) { + char *temp = (char *)REALLOC(tag->buf, bufSize); + if (temp == NULL) + return XML_FALSE; + /* if tag->name.str points to tag->buf (only when namespace + processing is off) then we have to update it + */ + if (tag->name.str == (XML_Char *)tag->buf) + tag->name.str = (XML_Char *)temp; + /* if tag->name.localPart is set (when namespace processing is on) + then update it as well, since it will always point into tag->buf + */ + if (tag->name.localPart) + tag->name.localPart = (XML_Char *)temp + (tag->name.localPart - + (XML_Char *)tag->buf); + tag->buf = temp; + tag->bufEnd = temp + bufSize; + rawNameBuf = temp + nameLen; + } + memcpy(rawNameBuf, tag->rawName, tag->rawNameLength); + tag->rawName = rawNameBuf; + tag = tag->parent; + } + return XML_TRUE; +} + +static enum XML_Error PTRCALL +contentProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doContent(parser, 0, encoding, start, end, + endPtr, (XML_Bool)!ps_finalBuffer); + if (result == XML_ERROR_NONE) { + if (!storeRawNames(parser)) + return XML_ERROR_NO_MEMORY; + } + return result; +} + +static enum XML_Error PTRCALL +externalEntityInitProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = initializeEncoding(parser); + if (result != XML_ERROR_NONE) + return result; + processor = externalEntityInitProcessor2; + return externalEntityInitProcessor2(parser, start, end, endPtr); +} + +static enum XML_Error PTRCALL +externalEntityInitProcessor2(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + const char *next = start; /* XmlContentTok doesn't always set the last arg */ + int tok = XmlContentTok(encoding, start, end, &next); + switch (tok) { + case XML_TOK_BOM: + /* If we are at the end of the buffer, this would cause the next stage, + i.e. externalEntityInitProcessor3, to pass control directly to + doContent (by detecting XML_TOK_NONE) without processing any xml text + declaration - causing the error XML_ERROR_MISPLACED_XML_PI in doContent. + */ + if (next == end && !ps_finalBuffer) { + *endPtr = next; + return XML_ERROR_NONE; + } + start = next; + break; + case XML_TOK_PARTIAL: + if (!ps_finalBuffer) { + *endPtr = start; + return XML_ERROR_NONE; + } + eventPtr = start; + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (!ps_finalBuffer) { + *endPtr = start; + return XML_ERROR_NONE; + } + eventPtr = start; + return XML_ERROR_PARTIAL_CHAR; + } + processor = externalEntityInitProcessor3; + return externalEntityInitProcessor3(parser, start, end, endPtr); +} + +static enum XML_Error PTRCALL +externalEntityInitProcessor3(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + int tok; + const char *next = start; /* XmlContentTok doesn't always set the last arg */ + eventPtr = start; + tok = XmlContentTok(encoding, start, end, &next); + eventEndPtr = next; + + switch (tok) { + case XML_TOK_XML_DECL: + { + enum XML_Error result; + result = processXmlDecl(parser, 1, start, next); + if (result != XML_ERROR_NONE) + return result; + switch (ps_parsing) { + case XML_SUSPENDED: + *endPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: + start = next; + } + } + break; + case XML_TOK_PARTIAL: + if (!ps_finalBuffer) { + *endPtr = start; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (!ps_finalBuffer) { + *endPtr = start; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + } + processor = externalEntityContentProcessor; + tagLevel = 1; + return externalEntityContentProcessor(parser, start, end, endPtr); +} + +static enum XML_Error PTRCALL +externalEntityContentProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doContent(parser, 1, encoding, start, end, + endPtr, (XML_Bool)!ps_finalBuffer); + if (result == XML_ERROR_NONE) { + if (!storeRawNames(parser)) + return XML_ERROR_NO_MEMORY; + } + return result; +} + +static enum XML_Error +doContent(XML_Parser parser, + int startTagLevel, + const ENCODING *enc, + const char *s, + const char *end, + const char **nextPtr, + XML_Bool haveMore) +{ + /* save one level of indirection */ + DTD * const dtd = _dtd; + + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + *eventPP = s; + + for (;;) { + const char *next = s; /* XmlContentTok doesn't always set the last arg */ + int tok = XmlContentTok(enc, s, end, &next); + *eventEndPP = next; + switch (tok) { + case XML_TOK_TRAILING_CR: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + *eventEndPP = end; + if (characterDataHandler) { + XML_Char c = 0xA; + characterDataHandler(handlerArg, &c, 1); + } + else if (defaultHandler) + reportDefault(parser, enc, s, end); + /* We are at the end of the final buffer, should we check for + XML_SUSPENDED, XML_FINISHED? + */ + if (startTagLevel == 0) + return XML_ERROR_NO_ELEMENTS; + if (tagLevel != startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + *nextPtr = end; + return XML_ERROR_NONE; + case XML_TOK_NONE: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + if (startTagLevel > 0) { + if (tagLevel != startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_NO_ELEMENTS; + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_ENTITY_REF: + { + const XML_Char *name; + ENTITY *entity; + XML_Char ch = (XML_Char) XmlPredefinedEntityName(enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (ch) { + if (characterDataHandler) + characterDataHandler(handlerArg, &ch, 1); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + name = poolStoreString(&dtd->pool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(&dtd->generalEntities, name, 0); + poolDiscard(&dtd->pool); + /* First, determine if a check for an existing declaration is needed; + if yes, check that the entity exists, and that it is internal, + otherwise call the skipped entity or default handler. + */ + if (!dtd->hasParamEntityRefs || dtd->standalone) { + if (!entity) + return XML_ERROR_UNDEFINED_ENTITY; + else if (!entity->is_internal) + return XML_ERROR_ENTITY_DECLARED_IN_PE; + } + else if (!entity) { + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, name, 0); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + if (entity->open) + return XML_ERROR_RECURSIVE_ENTITY_REF; + if (entity->notation) + return XML_ERROR_BINARY_ENTITY_REF; + if (entity->textPtr) { + enum XML_Error result; + if (!defaultExpandInternalEntities) { + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, entity->name, 0); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + result = processInternalEntity(parser, entity, XML_FALSE); + if (result != XML_ERROR_NONE) + return result; + } + else if (externalEntityRefHandler) { + const XML_Char *context; + entity->open = XML_TRUE; + context = getContext(parser); + entity->open = XML_FALSE; + if (!context) + return XML_ERROR_NO_MEMORY; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + context, + entity->base, + entity->systemId, + entity->publicId)) + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + poolDiscard(&tempPool); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + case XML_TOK_START_TAG_NO_ATTS: + /* fall through */ + case XML_TOK_START_TAG_WITH_ATTS: + { + TAG *tag; + enum XML_Error result; + XML_Char *toPtr; + if (freeTagList) { + tag = freeTagList; + freeTagList = freeTagList->parent; + } + else { + tag = (TAG *)MALLOC(sizeof(TAG)); + if (!tag) + return XML_ERROR_NO_MEMORY; + tag->buf = (char *)MALLOC(INIT_TAG_BUF_SIZE); + if (!tag->buf) { + FREE(tag); + return XML_ERROR_NO_MEMORY; + } + tag->bufEnd = tag->buf + INIT_TAG_BUF_SIZE; + } + tag->bindings = NULL; + tag->parent = tagStack; + tagStack = tag; + tag->name.localPart = NULL; + tag->name.prefix = NULL; + tag->rawName = s + enc->minBytesPerChar; + tag->rawNameLength = XmlNameLength(enc, tag->rawName); + ++tagLevel; + { + const char *rawNameEnd = tag->rawName + tag->rawNameLength; + const char *fromPtr = tag->rawName; + toPtr = (XML_Char *)tag->buf; + for (;;) { + int bufSize; + int convLen; + XmlConvert(enc, + &fromPtr, rawNameEnd, + (ICHAR **)&toPtr, (ICHAR *)tag->bufEnd - 1); + convLen = (int)(toPtr - (XML_Char *)tag->buf); + if (fromPtr == rawNameEnd) { + tag->name.strLen = convLen; + break; + } + bufSize = (int)(tag->bufEnd - tag->buf) << 1; + { + char *temp = (char *)REALLOC(tag->buf, bufSize); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + tag->buf = temp; + tag->bufEnd = temp + bufSize; + toPtr = (XML_Char *)temp + convLen; + } + } + } + tag->name.str = (XML_Char *)tag->buf; + *toPtr = XML_T('\0'); + result = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings)); + if (result) + return result; + if (startElementHandler) + startElementHandler(handlerArg, tag->name.str, + (const XML_Char **)atts); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + poolClear(&tempPool); + break; + } + case XML_TOK_EMPTY_ELEMENT_NO_ATTS: + /* fall through */ + case XML_TOK_EMPTY_ELEMENT_WITH_ATTS: + { + const char *rawName = s + enc->minBytesPerChar; + enum XML_Error result; + BINDING *bindings = NULL; + XML_Bool noElmHandlers = XML_TRUE; + TAG_NAME name; + name.str = poolStoreString(&tempPool, enc, rawName, + rawName + XmlNameLength(enc, rawName)); + if (!name.str) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + result = storeAtts(parser, enc, s, &name, &bindings); + if (result) + return result; + poolFinish(&tempPool); + if (startElementHandler) { + startElementHandler(handlerArg, name.str, (const XML_Char **)atts); + noElmHandlers = XML_FALSE; + } + if (endElementHandler) { + if (startElementHandler) + *eventPP = *eventEndPP; + endElementHandler(handlerArg, name.str); + noElmHandlers = XML_FALSE; + } + if (noElmHandlers && defaultHandler) + reportDefault(parser, enc, s, next); + poolClear(&tempPool); + while (bindings) { + BINDING *b = bindings; + if (endNamespaceDeclHandler) + endNamespaceDeclHandler(handlerArg, b->prefix->name); + bindings = bindings->nextTagBinding; + b->nextTagBinding = freeBindingList; + freeBindingList = b; + b->prefix->binding = b->prevPrefixBinding; + } + } + if (tagLevel == 0) + return epilogProcessor(parser, next, end, nextPtr); + break; + case XML_TOK_END_TAG: + if (tagLevel == startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + else { + int len; + const char *rawName; + TAG *tag = tagStack; + tagStack = tag->parent; + tag->parent = freeTagList; + freeTagList = tag; + rawName = s + enc->minBytesPerChar*2; + len = XmlNameLength(enc, rawName); + if (len != tag->rawNameLength + || memcmp(tag->rawName, rawName, len) != 0) { + *eventPP = rawName; + return XML_ERROR_TAG_MISMATCH; + } + --tagLevel; + if (endElementHandler) { + const XML_Char *localPart; + const XML_Char *prefix; + XML_Char *uri; + localPart = tag->name.localPart; + if (ns && localPart) { + /* localPart and prefix may have been overwritten in + tag->name.str, since this points to the binding->uri + buffer which gets re-used; so we have to add them again + */ + uri = (XML_Char *)tag->name.str + tag->name.uriLen; + /* don't need to check for space - already done in storeAtts() */ + while (*localPart) *uri++ = *localPart++; + prefix = (XML_Char *)tag->name.prefix; + if (ns_triplets && prefix) { + *uri++ = namespaceSeparator; + while (*prefix) *uri++ = *prefix++; + } + *uri = XML_T('\0'); + } + endElementHandler(handlerArg, tag->name.str); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + while (tag->bindings) { + BINDING *b = tag->bindings; + if (endNamespaceDeclHandler) + endNamespaceDeclHandler(handlerArg, b->prefix->name); + tag->bindings = tag->bindings->nextTagBinding; + b->nextTagBinding = freeBindingList; + freeBindingList = b; + b->prefix->binding = b->prevPrefixBinding; + } + if (tagLevel == 0) + return epilogProcessor(parser, next, end, nextPtr); + } + break; + case XML_TOK_CHAR_REF: + { + int n = XmlCharRefNumber(enc, s); + if (n < 0) + return XML_ERROR_BAD_CHAR_REF; + if (characterDataHandler) { + XML_Char buf[XML_ENCODE_MAX]; + characterDataHandler(handlerArg, buf, XmlEncode(n, (ICHAR *)buf)); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + } + break; + case XML_TOK_XML_DECL: + return XML_ERROR_MISPLACED_XML_PI; + case XML_TOK_DATA_NEWLINE: + if (characterDataHandler) { + XML_Char c = 0xA; + characterDataHandler(handlerArg, &c, 1); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + case XML_TOK_CDATA_SECT_OPEN: + { + enum XML_Error result; + if (startCdataSectionHandler) + startCdataSectionHandler(handlerArg); +#if 0 + /* Suppose you doing a transformation on a document that involves + changing only the character data. You set up a defaultHandler + and a characterDataHandler. The defaultHandler simply copies + characters through. The characterDataHandler does the + transformation and writes the characters out escaping them as + necessary. This case will fail to work if we leave out the + following two lines (because & and < inside CDATA sections will + be incorrectly escaped). + + However, now we have a start/endCdataSectionHandler, so it seems + easier to let the user deal with this. + */ + else if (characterDataHandler) + characterDataHandler(handlerArg, dataBuf, 0); +#endif + else if (defaultHandler) + reportDefault(parser, enc, s, next); + result = doCdataSection(parser, enc, &next, end, nextPtr, haveMore); + if (result != XML_ERROR_NONE) + return result; + else if (!next) { + processor = cdataSectionProcessor; + return result; + } + } + break; + case XML_TOK_TRAILING_RSQB: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + if (characterDataHandler) { + if (MUST_CONVERT(enc, s)) { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd); + characterDataHandler(handlerArg, dataBuf, + (int)(dataPtr - (ICHAR *)dataBuf)); + } + else + characterDataHandler(handlerArg, + (XML_Char *)s, + (int)((XML_Char *)end - (XML_Char *)s)); + } + else if (defaultHandler) + reportDefault(parser, enc, s, end); + /* We are at the end of the final buffer, should we check for + XML_SUSPENDED, XML_FINISHED? + */ + if (startTagLevel == 0) { + *eventPP = end; + return XML_ERROR_NO_ELEMENTS; + } + if (tagLevel != startTagLevel) { + *eventPP = end; + return XML_ERROR_ASYNC_ENTITY; + } + *nextPtr = end; + return XML_ERROR_NONE; + case XML_TOK_DATA_CHARS: + if (characterDataHandler) { + if (MUST_CONVERT(enc, s)) { + for (;;) { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd); + *eventEndPP = s; + characterDataHandler(handlerArg, dataBuf, + (int)(dataPtr - (ICHAR *)dataBuf)); + if (s == next) + break; + *eventPP = s; + } + } + else + characterDataHandler(handlerArg, + (XML_Char *)s, + (int)((XML_Char *)next - (XML_Char *)s)); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + case XML_TOK_PI: + if (!reportProcessingInstruction(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_COMMENT: + if (!reportComment(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + break; + default: + if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + *eventPP = s = next; + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: ; + } + } + /* not reached */ +} + +/* Precondition: all arguments must be non-NULL; + Purpose: + - normalize attributes + - check attributes for well-formedness + - generate namespace aware attribute names (URI, prefix) + - build list of attributes for startElementHandler + - default attributes + - process namespace declarations (check and report them) + - generate namespace aware element name (URI, prefix) +*/ +static enum XML_Error +storeAtts(XML_Parser parser, const ENCODING *enc, + const char *attStr, TAG_NAME *tagNamePtr, + BINDING **bindingsPtr) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + ELEMENT_TYPE *elementType; + int nDefaultAtts; + const XML_Char **appAtts; /* the attribute list for the application */ + int attIndex = 0; + int prefixLen; + int i; + int n; + XML_Char *uri; + int nPrefixes = 0; + BINDING *binding; + const XML_Char *localPart; + + /* lookup the element type name */ + elementType = (ELEMENT_TYPE *)lookup(&dtd->elementTypes, tagNamePtr->str,0); + if (!elementType) { + const XML_Char *name = poolCopyString(&dtd->pool, tagNamePtr->str); + if (!name) + return XML_ERROR_NO_MEMORY; + elementType = (ELEMENT_TYPE *)lookup(&dtd->elementTypes, name, + sizeof(ELEMENT_TYPE)); + if (!elementType) + return XML_ERROR_NO_MEMORY; + if (ns && !setElementTypePrefix(parser, elementType)) + return XML_ERROR_NO_MEMORY; + } + nDefaultAtts = elementType->nDefaultAtts; + + /* get the attributes from the tokenizer */ + n = XmlGetAttributes(enc, attStr, attsSize, atts); + if (n + nDefaultAtts > attsSize) { + int oldAttsSize = attsSize; + ATTRIBUTE *temp; + attsSize = n + nDefaultAtts + INIT_ATTS_SIZE; + temp = (ATTRIBUTE *)REALLOC((void *)atts, attsSize * sizeof(ATTRIBUTE)); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + atts = temp; + if (n > oldAttsSize) + XmlGetAttributes(enc, attStr, n, atts); + } + + appAtts = (const XML_Char **)atts; + for (i = 0; i < n; i++) { + /* add the name and value to the attribute list */ + ATTRIBUTE_ID *attId = getAttributeId(parser, enc, atts[i].name, + atts[i].name + + XmlNameLength(enc, atts[i].name)); + if (!attId) + return XML_ERROR_NO_MEMORY; + /* Detect duplicate attributes by their QNames. This does not work when + namespace processing is turned on and different prefixes for the same + namespace are used. For this case we have a check further down. + */ + if ((attId->name)[-1]) { + if (enc == encoding) + eventPtr = atts[i].name; + return XML_ERROR_DUPLICATE_ATTRIBUTE; + } + (attId->name)[-1] = 1; + appAtts[attIndex++] = attId->name; + if (!atts[i].normalized) { + enum XML_Error result; + XML_Bool isCdata = XML_TRUE; + + /* figure out whether declared as other than CDATA */ + if (attId->maybeTokenized) { + int j; + for (j = 0; j < nDefaultAtts; j++) { + if (attId == elementType->defaultAtts[j].id) { + isCdata = elementType->defaultAtts[j].isCdata; + break; + } + } + } + + /* normalize the attribute value */ + result = storeAttributeValue(parser, enc, isCdata, + atts[i].valuePtr, atts[i].valueEnd, + &tempPool); + if (result) + return result; + appAtts[attIndex] = poolStart(&tempPool); + poolFinish(&tempPool); + } + else { + /* the value did not need normalizing */ + appAtts[attIndex] = poolStoreString(&tempPool, enc, atts[i].valuePtr, + atts[i].valueEnd); + if (appAtts[attIndex] == 0) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + } + /* handle prefixed attribute names */ + if (attId->prefix) { + if (attId->xmlns) { + /* deal with namespace declarations here */ + enum XML_Error result = addBinding(parser, attId->prefix, attId, + appAtts[attIndex], bindingsPtr); + if (result) + return result; + --attIndex; + } + else { + /* deal with other prefixed names later */ + attIndex++; + nPrefixes++; + (attId->name)[-1] = 2; + } + } + else + attIndex++; + } + + /* set-up for XML_GetSpecifiedAttributeCount and XML_GetIdAttributeIndex */ + nSpecifiedAtts = attIndex; + if (elementType->idAtt && (elementType->idAtt->name)[-1]) { + for (i = 0; i < attIndex; i += 2) + if (appAtts[i] == elementType->idAtt->name) { + idAttIndex = i; + break; + } + } + else + idAttIndex = -1; + + /* do attribute defaulting */ + for (i = 0; i < nDefaultAtts; i++) { + const DEFAULT_ATTRIBUTE *da = elementType->defaultAtts + i; + if (!(da->id->name)[-1] && da->value) { + if (da->id->prefix) { + if (da->id->xmlns) { + enum XML_Error result = addBinding(parser, da->id->prefix, da->id, + da->value, bindingsPtr); + if (result) + return result; + } + else { + (da->id->name)[-1] = 2; + nPrefixes++; + appAtts[attIndex++] = da->id->name; + appAtts[attIndex++] = da->value; + } + } + else { + (da->id->name)[-1] = 1; + appAtts[attIndex++] = da->id->name; + appAtts[attIndex++] = da->value; + } + } + } + appAtts[attIndex] = 0; + + /* expand prefixed attribute names, check for duplicates, + and clear flags that say whether attributes were specified */ + i = 0; + if (nPrefixes) { + int j; /* hash table index */ + unsigned long version = nsAttsVersion; + int nsAttsSize = (int)1 << nsAttsPower; + /* size of hash table must be at least 2 * (# of prefixed attributes) */ + if ((nPrefixes << 1) >> nsAttsPower) { /* true for nsAttsPower = 0 */ + NS_ATT *temp; + /* hash table size must also be a power of 2 and >= 8 */ + while (nPrefixes >> nsAttsPower++); + if (nsAttsPower < 3) + nsAttsPower = 3; + nsAttsSize = (int)1 << nsAttsPower; + temp = (NS_ATT *)REALLOC(nsAtts, nsAttsSize * sizeof(NS_ATT)); + if (!temp) + return XML_ERROR_NO_MEMORY; + nsAtts = temp; + version = 0; /* force re-initialization of nsAtts hash table */ + } + /* using a version flag saves us from initializing nsAtts every time */ + if (!version) { /* initialize version flags when version wraps around */ + version = INIT_ATTS_VERSION; + for (j = nsAttsSize; j != 0; ) + nsAtts[--j].version = version; + } + nsAttsVersion = --version; + + /* expand prefixed names and check for duplicates */ + for (; i < attIndex; i += 2) { + const XML_Char *s = appAtts[i]; + if (s[-1] == 2) { /* prefixed */ + ATTRIBUTE_ID *id; + const BINDING *b; + unsigned long uriHash = 0; + ((XML_Char *)s)[-1] = 0; /* clear flag */ + id = (ATTRIBUTE_ID *)lookup(&dtd->attributeIds, s, 0); + b = id->prefix->binding; + if (!b) + return XML_ERROR_UNBOUND_PREFIX; + + /* as we expand the name we also calculate its hash value */ + for (j = 0; j < b->uriLen; j++) { + const XML_Char c = b->uri[j]; + if (!poolAppendChar(&tempPool, c)) + return XML_ERROR_NO_MEMORY; + uriHash = CHAR_HASH(uriHash, c); + } + while (*s++ != XML_T(':')) + ; + do { /* copies null terminator */ + const XML_Char c = *s; + if (!poolAppendChar(&tempPool, *s)) + return XML_ERROR_NO_MEMORY; + uriHash = CHAR_HASH(uriHash, c); + } while (*s++); + + { /* Check hash table for duplicate of expanded name (uriName). + Derived from code in lookup(HASH_TABLE *table, ...). + */ + unsigned char step = 0; + unsigned long mask = nsAttsSize - 1; + j = uriHash & mask; /* index into hash table */ + while (nsAtts[j].version == version) { + /* for speed we compare stored hash values first */ + if (uriHash == nsAtts[j].hash) { + const XML_Char *s1 = poolStart(&tempPool); + const XML_Char *s2 = nsAtts[j].uriName; + /* s1 is null terminated, but not s2 */ + for (; *s1 == *s2 && *s1 != 0; s1++, s2++); + if (*s1 == 0) + return XML_ERROR_DUPLICATE_ATTRIBUTE; + } + if (!step) + step = PROBE_STEP(uriHash, mask, nsAttsPower); + j < step ? (j += nsAttsSize - step) : (j -= step); + } + } + + if (ns_triplets) { /* append namespace separator and prefix */ + tempPool.ptr[-1] = namespaceSeparator; + s = b->prefix->name; + do { + if (!poolAppendChar(&tempPool, *s)) + return XML_ERROR_NO_MEMORY; + } while (*s++); + } + + /* store expanded name in attribute list */ + s = poolStart(&tempPool); + poolFinish(&tempPool); + appAtts[i] = s; + + /* fill empty slot with new version, uriName and hash value */ + nsAtts[j].version = version; + nsAtts[j].hash = uriHash; + nsAtts[j].uriName = s; + + if (!--nPrefixes) { + i += 2; + break; + } + } + else /* not prefixed */ + ((XML_Char *)s)[-1] = 0; /* clear flag */ + } + } + /* clear flags for the remaining attributes */ + for (; i < attIndex; i += 2) + ((XML_Char *)(appAtts[i]))[-1] = 0; + for (binding = *bindingsPtr; binding; binding = binding->nextTagBinding) + binding->attId->name[-1] = 0; + + if (!ns) + return XML_ERROR_NONE; + + /* expand the element type name */ + if (elementType->prefix) { + binding = elementType->prefix->binding; + if (!binding) + return XML_ERROR_UNBOUND_PREFIX; + localPart = tagNamePtr->str; + while (*localPart++ != XML_T(':')) + ; + } + else if (dtd->defaultPrefix.binding) { + binding = dtd->defaultPrefix.binding; + localPart = tagNamePtr->str; + } + else + return XML_ERROR_NONE; + prefixLen = 0; + if (ns_triplets && binding->prefix->name) { + for (; binding->prefix->name[prefixLen++];) + ; /* prefixLen includes null terminator */ + } + tagNamePtr->localPart = localPart; + tagNamePtr->uriLen = binding->uriLen; + tagNamePtr->prefix = binding->prefix->name; + tagNamePtr->prefixLen = prefixLen; + for (i = 0; localPart[i++];) + ; /* i includes null terminator */ + n = i + binding->uriLen + prefixLen; + if (n > binding->uriAlloc) { + TAG *p; + uri = (XML_Char *)MALLOC((n + EXPAND_SPARE) * sizeof(XML_Char)); + if (!uri) + return XML_ERROR_NO_MEMORY; + binding->uriAlloc = n + EXPAND_SPARE; + memcpy(uri, binding->uri, binding->uriLen * sizeof(XML_Char)); + for (p = tagStack; p; p = p->parent) + if (p->name.str == binding->uri) + p->name.str = uri; + FREE(binding->uri); + binding->uri = uri; + } + /* if namespaceSeparator != '\0' then uri includes it already */ + uri = binding->uri + binding->uriLen; + memcpy(uri, localPart, i * sizeof(XML_Char)); + /* we always have a namespace separator between localPart and prefix */ + if (prefixLen) { + uri += i - 1; + *uri = namespaceSeparator; /* replace null terminator */ + memcpy(uri + 1, binding->prefix->name, prefixLen * sizeof(XML_Char)); + } + tagNamePtr->str = binding->uri; + return XML_ERROR_NONE; +} + +/* addBinding() overwrites the value of prefix->binding without checking. + Therefore one must keep track of the old value outside of addBinding(). +*/ +static enum XML_Error +addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, + const XML_Char *uri, BINDING **bindingsPtr) +{ + static const XML_Char xmlNamespace[] = { + 'h', 't', 't', 'p', ':', '/', '/', + 'w', 'w', 'w', '.', 'w', '3', '.', 'o', 'r', 'g', '/', + 'X', 'M', 'L', '/', '1', '9', '9', '8', '/', + 'n', 'a', 'm', 'e', 's', 'p', 'a', 'c', 'e', '\0' + }; + static const int xmlLen = + (int)sizeof(xmlNamespace)/sizeof(XML_Char) - 1; + static const XML_Char xmlnsNamespace[] = { + 'h', 't', 't', 'p', ':', '/', '/', + 'w', 'w', 'w', '.', 'w', '3', '.', 'o', 'r', 'g', '/', + '2', '0', '0', '0', '/', 'x', 'm', 'l', 'n', 's', '/', '\0' + }; + static const int xmlnsLen = + (int)sizeof(xmlnsNamespace)/sizeof(XML_Char) - 1; + + XML_Bool mustBeXML = XML_FALSE; + XML_Bool isXML = XML_TRUE; + XML_Bool isXMLNS = XML_TRUE; + + BINDING *b; + int len; + + /* empty URI is only valid for default namespace per XML NS 1.0 (not 1.1) */ + if (*uri == XML_T('\0') && prefix->name) + return XML_ERROR_UNDECLARING_PREFIX; + + if (prefix->name + && prefix->name[0] == XML_T('x') + && prefix->name[1] == XML_T('m') + && prefix->name[2] == XML_T('l')) { + + /* Not allowed to bind xmlns */ + if (prefix->name[3] == XML_T('n') + && prefix->name[4] == XML_T('s') + && prefix->name[5] == XML_T('\0')) + return XML_ERROR_RESERVED_PREFIX_XMLNS; + + if (prefix->name[3] == XML_T('\0')) + mustBeXML = XML_TRUE; + } + + for (len = 0; uri[len]; len++) { + if (isXML && (len > xmlLen || uri[len] != xmlNamespace[len])) + isXML = XML_FALSE; + + if (!mustBeXML && isXMLNS + && (len > xmlnsLen || uri[len] != xmlnsNamespace[len])) + isXMLNS = XML_FALSE; + } + isXML = isXML && len == xmlLen; + isXMLNS = isXMLNS && len == xmlnsLen; + + if (mustBeXML != isXML) + return mustBeXML ? XML_ERROR_RESERVED_PREFIX_XML + : XML_ERROR_RESERVED_NAMESPACE_URI; + + if (isXMLNS) + return XML_ERROR_RESERVED_NAMESPACE_URI; + + if (namespaceSeparator) + len++; + if (freeBindingList) { + b = freeBindingList; + if (len > b->uriAlloc) { + XML_Char *temp = (XML_Char *)REALLOC(b->uri, + sizeof(XML_Char) * (len + EXPAND_SPARE)); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + b->uri = temp; + b->uriAlloc = len + EXPAND_SPARE; + } + freeBindingList = b->nextTagBinding; + } + else { + b = (BINDING *)MALLOC(sizeof(BINDING)); + if (!b) + return XML_ERROR_NO_MEMORY; + b->uri = (XML_Char *)MALLOC(sizeof(XML_Char) * (len + EXPAND_SPARE)); + if (!b->uri) { + FREE(b); + return XML_ERROR_NO_MEMORY; + } + b->uriAlloc = len + EXPAND_SPARE; + } + b->uriLen = len; + memcpy(b->uri, uri, len * sizeof(XML_Char)); + if (namespaceSeparator) + b->uri[len - 1] = namespaceSeparator; + b->prefix = prefix; + b->attId = attId; + b->prevPrefixBinding = prefix->binding; + /* NULL binding when default namespace undeclared */ + if (*uri == XML_T('\0') && prefix == &_dtd->defaultPrefix) + prefix->binding = NULL; + else + prefix->binding = b; + b->nextTagBinding = *bindingsPtr; + *bindingsPtr = b; + /* if attId == NULL then we are not starting a namespace scope */ + if (attId && startNamespaceDeclHandler) + startNamespaceDeclHandler(handlerArg, prefix->name, + prefix->binding ? uri : 0); + return XML_ERROR_NONE; +} + +/* The idea here is to avoid using stack for each CDATA section when + the whole file is parsed with one call. +*/ +static enum XML_Error PTRCALL +cdataSectionProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doCdataSection(parser, encoding, &start, end, + endPtr, (XML_Bool)!ps_finalBuffer); + if (result != XML_ERROR_NONE) + return result; + if (start) { + if (parentParser) { /* we are parsing an external entity */ + processor = externalEntityContentProcessor; + return externalEntityContentProcessor(parser, start, end, endPtr); + } + else { + processor = contentProcessor; + return contentProcessor(parser, start, end, endPtr); + } + } + return result; +} + +/* startPtr gets set to non-null if the section is closed, and to null if + the section is not yet closed. +*/ +static enum XML_Error +doCdataSection(XML_Parser parser, + const ENCODING *enc, + const char **startPtr, + const char *end, + const char **nextPtr, + XML_Bool haveMore) +{ + const char *s = *startPtr; + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + *eventPP = s; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + *eventPP = s; + *startPtr = NULL; + + for (;;) { + const char *next; + int tok = XmlCdataSectionTok(enc, s, end, &next); + *eventEndPP = next; + switch (tok) { + case XML_TOK_CDATA_SECT_CLOSE: + if (endCdataSectionHandler) + endCdataSectionHandler(handlerArg); +#if 0 + /* see comment under XML_TOK_CDATA_SECT_OPEN */ + else if (characterDataHandler) + characterDataHandler(handlerArg, dataBuf, 0); +#endif + else if (defaultHandler) + reportDefault(parser, enc, s, next); + *startPtr = next; + *nextPtr = next; + if (ps_parsing == XML_FINISHED) + return XML_ERROR_ABORTED; + else + return XML_ERROR_NONE; + case XML_TOK_DATA_NEWLINE: + if (characterDataHandler) { + XML_Char c = 0xA; + characterDataHandler(handlerArg, &c, 1); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + case XML_TOK_DATA_CHARS: + if (characterDataHandler) { + if (MUST_CONVERT(enc, s)) { + for (;;) { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd); + *eventEndPP = next; + characterDataHandler(handlerArg, dataBuf, + (int)(dataPtr - (ICHAR *)dataBuf)); + if (s == next) + break; + *eventPP = s; + } + } + else + characterDataHandler(handlerArg, + (XML_Char *)s, + (int)((XML_Char *)next - (XML_Char *)s)); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_PARTIAL: + case XML_TOK_NONE: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_CDATA_SECTION; + default: + *eventPP = next; + return XML_ERROR_UNEXPECTED_STATE; + } + + *eventPP = s = next; + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: ; + } + } + /* not reached */ +} + +#ifdef XML_DTD + +/* The idea here is to avoid using stack for each IGNORE section when + the whole file is parsed with one call. +*/ +static enum XML_Error PTRCALL +ignoreSectionProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doIgnoreSection(parser, encoding, &start, end, + endPtr, (XML_Bool)!ps_finalBuffer); + if (result != XML_ERROR_NONE) + return result; + if (start) { + processor = prologProcessor; + return prologProcessor(parser, start, end, endPtr); + } + return result; +} + +/* startPtr gets set to non-null is the section is closed, and to null + if the section is not yet closed. +*/ +static enum XML_Error +doIgnoreSection(XML_Parser parser, + const ENCODING *enc, + const char **startPtr, + const char *end, + const char **nextPtr, + XML_Bool haveMore) +{ + const char *next; + int tok; + const char *s = *startPtr; + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + *eventPP = s; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + *eventPP = s; + *startPtr = NULL; + tok = XmlIgnoreSectionTok(enc, s, end, &next); + *eventEndPP = next; + switch (tok) { + case XML_TOK_IGNORE_SECT: + if (defaultHandler) + reportDefault(parser, enc, s, next); + *startPtr = next; + *nextPtr = next; + if (ps_parsing == XML_FINISHED) + return XML_ERROR_ABORTED; + else + return XML_ERROR_NONE; + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_PARTIAL: + case XML_TOK_NONE: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_SYNTAX; /* XML_ERROR_UNCLOSED_IGNORE_SECTION */ + default: + *eventPP = next; + return XML_ERROR_UNEXPECTED_STATE; + } + /* not reached */ +} + +#endif /* XML_DTD */ + +static enum XML_Error +initializeEncoding(XML_Parser parser) +{ + const char *s; +#ifdef XML_UNICODE + char encodingBuf[128]; + if (!protocolEncodingName) + s = NULL; + else { + int i; + for (i = 0; protocolEncodingName[i]; i++) { + if (i == sizeof(encodingBuf) - 1 + || (protocolEncodingName[i] & ~0x7f) != 0) { + encodingBuf[0] = '\0'; + break; + } + encodingBuf[i] = (char)protocolEncodingName[i]; + } + encodingBuf[i] = '\0'; + s = encodingBuf; + } +#else + s = protocolEncodingName; +#endif + if ((ns ? XmlInitEncodingNS : XmlInitEncoding)(&initEncoding, &encoding, s)) + return XML_ERROR_NONE; + return handleUnknownEncoding(parser, protocolEncodingName); +} + +static enum XML_Error +processXmlDecl(XML_Parser parser, int isGeneralTextEntity, + const char *s, const char *next) +{ + const char *encodingName = NULL; + const XML_Char *storedEncName = NULL; + const ENCODING *newEncoding = NULL; + const char *version = NULL; + const char *versionend; + const XML_Char *storedversion = NULL; + int standalone = -1; + if (!(ns + ? XmlParseXmlDeclNS + : XmlParseXmlDecl)(isGeneralTextEntity, + encoding, + s, + next, + &eventPtr, + &version, + &versionend, + &encodingName, + &newEncoding, + &standalone)) { + if (isGeneralTextEntity) + return XML_ERROR_TEXT_DECL; + else + return XML_ERROR_XML_DECL; + } + if (!isGeneralTextEntity && standalone == 1) { + _dtd->standalone = XML_TRUE; +#ifdef XML_DTD + if (paramEntityParsing == XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE) + paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; +#endif /* XML_DTD */ + } + if (xmlDeclHandler) { + if (encodingName != NULL) { + storedEncName = poolStoreString(&temp2Pool, + encoding, + encodingName, + encodingName + + XmlNameLength(encoding, encodingName)); + if (!storedEncName) + return XML_ERROR_NO_MEMORY; + poolFinish(&temp2Pool); + } + if (version) { + storedversion = poolStoreString(&temp2Pool, + encoding, + version, + versionend - encoding->minBytesPerChar); + if (!storedversion) + return XML_ERROR_NO_MEMORY; + } + xmlDeclHandler(handlerArg, storedversion, storedEncName, standalone); + } + else if (defaultHandler) + reportDefault(parser, encoding, s, next); + if (protocolEncodingName == NULL) { + if (newEncoding) { + if (newEncoding->minBytesPerChar != encoding->minBytesPerChar) { + eventPtr = encodingName; + return XML_ERROR_INCORRECT_ENCODING; + } + encoding = newEncoding; + } + else if (encodingName) { + enum XML_Error result; + if (!storedEncName) { + storedEncName = poolStoreString( + &temp2Pool, encoding, encodingName, + encodingName + XmlNameLength(encoding, encodingName)); + if (!storedEncName) + return XML_ERROR_NO_MEMORY; + } + result = handleUnknownEncoding(parser, storedEncName); + poolClear(&temp2Pool); + if (result == XML_ERROR_UNKNOWN_ENCODING) + eventPtr = encodingName; + return result; + } + } + + if (storedEncName || storedversion) + poolClear(&temp2Pool); + + return XML_ERROR_NONE; +} + +static enum XML_Error +handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName) +{ + if (unknownEncodingHandler) { + XML_Encoding info; + int i; + for (i = 0; i < 256; i++) + info.map[i] = -1; + info.convert = NULL; + info.data = NULL; + info.release = NULL; + if (unknownEncodingHandler(unknownEncodingHandlerData, encodingName, + &info)) { + ENCODING *enc; + unknownEncodingMem = MALLOC(XmlSizeOfUnknownEncoding()); + if (!unknownEncodingMem) { + if (info.release) + info.release(info.data); + return XML_ERROR_NO_MEMORY; + } + enc = (ns + ? XmlInitUnknownEncodingNS + : XmlInitUnknownEncoding)(unknownEncodingMem, + info.map, + info.convert, + info.data); + if (enc) { + unknownEncodingData = info.data; + unknownEncodingRelease = info.release; + encoding = enc; + return XML_ERROR_NONE; + } + } + if (info.release != NULL) + info.release(info.data); + } + return XML_ERROR_UNKNOWN_ENCODING; +} + +static enum XML_Error PTRCALL +prologInitProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + enum XML_Error result = initializeEncoding(parser); + if (result != XML_ERROR_NONE) + return result; + processor = prologProcessor; + return prologProcessor(parser, s, end, nextPtr); +} + +#ifdef XML_DTD + +static enum XML_Error PTRCALL +externalParEntInitProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + enum XML_Error result = initializeEncoding(parser); + if (result != XML_ERROR_NONE) + return result; + + /* we know now that XML_Parse(Buffer) has been called, + so we consider the external parameter entity read */ + _dtd->paramEntityRead = XML_TRUE; + + if (prologState.inEntityValue) { + processor = entityValueInitProcessor; + return entityValueInitProcessor(parser, s, end, nextPtr); + } + else { + processor = externalParEntProcessor; + return externalParEntProcessor(parser, s, end, nextPtr); + } +} + +static enum XML_Error PTRCALL +entityValueInitProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + int tok; + const char *start = s; + const char *next = start; + eventPtr = start; + + for (;;) { + tok = XmlPrologTok(encoding, start, end, &next); + eventEndPtr = next; + if (tok <= 0) { + if (!ps_finalBuffer && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_NONE: /* start == end */ + default: + break; + } + /* found end of entity value - can store it now */ + return storeEntityValue(parser, encoding, s, end); + } + else if (tok == XML_TOK_XML_DECL) { + enum XML_Error result; + result = processXmlDecl(parser, 0, start, next); + if (result != XML_ERROR_NONE) + return result; + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: + *nextPtr = next; + } + /* stop scanning for text declaration - we found one */ + processor = entityValueProcessor; + return entityValueProcessor(parser, next, end, nextPtr); + } + /* If we are at the end of the buffer, this would cause XmlPrologTok to + return XML_TOK_NONE on the next call, which would then cause the + function to exit with *nextPtr set to s - that is what we want for other + tokens, but not for the BOM - we would rather like to skip it; + then, when this routine is entered the next time, XmlPrologTok will + return XML_TOK_INVALID, since the BOM is still in the buffer + */ + else if (tok == XML_TOK_BOM && next == end && !ps_finalBuffer) { + *nextPtr = next; + return XML_ERROR_NONE; + } + start = next; + eventPtr = start; + } +} + +static enum XML_Error PTRCALL +externalParEntProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + const char *next = s; + int tok; + + tok = XmlPrologTok(encoding, s, end, &next); + if (tok <= 0) { + if (!ps_finalBuffer && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_NONE: /* start == end */ + default: + break; + } + } + /* This would cause the next stage, i.e. doProlog to be passed XML_TOK_BOM. + However, when parsing an external subset, doProlog will not accept a BOM + as valid, and report a syntax error, so we have to skip the BOM + */ + else if (tok == XML_TOK_BOM) { + s = next; + tok = XmlPrologTok(encoding, s, end, &next); + } + + processor = prologProcessor; + return doProlog(parser, encoding, s, end, tok, next, + nextPtr, (XML_Bool)!ps_finalBuffer); +} + +static enum XML_Error PTRCALL +entityValueProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + const char *start = s; + const char *next = s; + const ENCODING *enc = encoding; + int tok; + + for (;;) { + tok = XmlPrologTok(enc, start, end, &next); + if (tok <= 0) { + if (!ps_finalBuffer && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_NONE: /* start == end */ + default: + break; + } + /* found end of entity value - can store it now */ + return storeEntityValue(parser, enc, s, end); + } + start = next; + } +} + +#endif /* XML_DTD */ + +static enum XML_Error PTRCALL +prologProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + const char *next = s; + int tok = XmlPrologTok(encoding, s, end, &next); + return doProlog(parser, encoding, s, end, tok, next, + nextPtr, (XML_Bool)!ps_finalBuffer); +} + +static enum XML_Error +doProlog(XML_Parser parser, + const ENCODING *enc, + const char *s, + const char *end, + int tok, + const char *next, + const char **nextPtr, + XML_Bool haveMore) +{ +#ifdef XML_DTD + static const XML_Char externalSubsetName[] = { '#' , '\0' }; +#endif /* XML_DTD */ + static const XML_Char atypeCDATA[] = { 'C', 'D', 'A', 'T', 'A', '\0' }; + static const XML_Char atypeID[] = { 'I', 'D', '\0' }; + static const XML_Char atypeIDREF[] = { 'I', 'D', 'R', 'E', 'F', '\0' }; + static const XML_Char atypeIDREFS[] = { 'I', 'D', 'R', 'E', 'F', 'S', '\0' }; + static const XML_Char atypeENTITY[] = { 'E', 'N', 'T', 'I', 'T', 'Y', '\0' }; + static const XML_Char atypeENTITIES[] = + { 'E', 'N', 'T', 'I', 'T', 'I', 'E', 'S', '\0' }; + static const XML_Char atypeNMTOKEN[] = { + 'N', 'M', 'T', 'O', 'K', 'E', 'N', '\0' }; + static const XML_Char atypeNMTOKENS[] = { + 'N', 'M', 'T', 'O', 'K', 'E', 'N', 'S', '\0' }; + static const XML_Char notationPrefix[] = { + 'N', 'O', 'T', 'A', 'T', 'I', 'O', 'N', '(', '\0' }; + static const XML_Char enumValueSep[] = { '|', '\0' }; + static const XML_Char enumValueStart[] = { '(', '\0' }; + + /* save one level of indirection */ + DTD * const dtd = _dtd; + + const char **eventPP; + const char **eventEndPP; + enum XML_Content_Quant quant; + + if (enc == encoding) { + eventPP = &eventPtr; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + + for (;;) { + int role; + XML_Bool handleDefault = XML_TRUE; + *eventPP = s; + *eventEndPP = next; + if (tok <= 0) { + if (haveMore && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_NONE: +#ifdef XML_DTD + /* for internal PE NOT referenced between declarations */ + if (enc != encoding && !openInternalEntities->betweenDecl) { + *nextPtr = s; + return XML_ERROR_NONE; + } + /* WFC: PE Between Declarations - must check that PE contains + complete markup, not only for external PEs, but also for + internal PEs if the reference occurs between declarations. + */ + if (isParamEntity || enc != encoding) { + if (XmlTokenRole(&prologState, XML_TOK_NONE, end, end, enc) + == XML_ROLE_ERROR) + return XML_ERROR_INCOMPLETE_PE; + *nextPtr = s; + return XML_ERROR_NONE; + } +#endif /* XML_DTD */ + return XML_ERROR_NO_ELEMENTS; + default: + tok = -tok; + next = end; + break; + } + } + role = XmlTokenRole(&prologState, tok, s, next, enc); + switch (role) { + case XML_ROLE_XML_DECL: + { + enum XML_Error result = processXmlDecl(parser, 0, s, next); + if (result != XML_ERROR_NONE) + return result; + enc = encoding; + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_DOCTYPE_NAME: + if (startDoctypeDeclHandler) { + doctypeName = poolStoreString(&tempPool, enc, s, next); + if (!doctypeName) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + doctypePubid = NULL; + handleDefault = XML_FALSE; + } + doctypeSysid = NULL; /* always initialize to NULL */ + break; + case XML_ROLE_DOCTYPE_INTERNAL_SUBSET: + if (startDoctypeDeclHandler) { + startDoctypeDeclHandler(handlerArg, doctypeName, doctypeSysid, + doctypePubid, 1); + doctypeName = NULL; + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + break; +#ifdef XML_DTD + case XML_ROLE_TEXT_DECL: + { + enum XML_Error result = processXmlDecl(parser, 1, s, next); + if (result != XML_ERROR_NONE) + return result; + enc = encoding; + handleDefault = XML_FALSE; + } + break; +#endif /* XML_DTD */ + case XML_ROLE_DOCTYPE_PUBLIC_ID: +#ifdef XML_DTD + useForeignDTD = XML_FALSE; + declEntity = (ENTITY *)lookup(&dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; +#endif /* XML_DTD */ + dtd->hasParamEntityRefs = XML_TRUE; + if (startDoctypeDeclHandler) { + XML_Char *tem; + if (!XmlIsPublicId(enc, s, next, eventPP)) + return XML_ERROR_PUBLICID; + tem = poolStoreString(&tempPool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!tem) + return XML_ERROR_NO_MEMORY; + normalizePublicId(tem); + doctypePubid = tem; + poolFinish(&tempPool); + handleDefault = XML_FALSE; + goto alreadyChecked; + } + /* fall through */ + case XML_ROLE_ENTITY_PUBLIC_ID: + if (!XmlIsPublicId(enc, s, next, eventPP)) + return XML_ERROR_PUBLICID; + alreadyChecked: + if (dtd->keepProcessing && declEntity) { + XML_Char *tem = poolStoreString(&dtd->pool, + enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!tem) + return XML_ERROR_NO_MEMORY; + normalizePublicId(tem); + declEntity->publicId = tem; + poolFinish(&dtd->pool); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_DOCTYPE_CLOSE: + if (doctypeName) { + startDoctypeDeclHandler(handlerArg, doctypeName, + doctypeSysid, doctypePubid, 0); + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + /* doctypeSysid will be non-NULL in the case of a previous + XML_ROLE_DOCTYPE_SYSTEM_ID, even if startDoctypeDeclHandler + was not set, indicating an external subset + */ +#ifdef XML_DTD + if (doctypeSysid || useForeignDTD) { + XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs; + dtd->hasParamEntityRefs = XML_TRUE; + if (paramEntityParsing && externalEntityRefHandler) { + ENTITY *entity = (ENTITY *)lookup(&dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!entity) + return XML_ERROR_NO_MEMORY; + if (useForeignDTD) + entity->base = curBase; + dtd->paramEntityRead = XML_FALSE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + if (dtd->paramEntityRead) { + if (!dtd->standalone && + notStandaloneHandler && + !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; + } + /* if we didn't read the foreign DTD then this means that there + is no external subset and we must reset dtd->hasParamEntityRefs + */ + else if (!doctypeSysid) + dtd->hasParamEntityRefs = hadParamEntityRefs; + /* end of DTD - no need to update dtd->keepProcessing */ + } + useForeignDTD = XML_FALSE; + } +#endif /* XML_DTD */ + if (endDoctypeDeclHandler) { + endDoctypeDeclHandler(handlerArg); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_INSTANCE_START: +#ifdef XML_DTD + /* if there is no DOCTYPE declaration then now is the + last chance to read the foreign DTD + */ + if (useForeignDTD) { + XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs; + dtd->hasParamEntityRefs = XML_TRUE; + if (paramEntityParsing && externalEntityRefHandler) { + ENTITY *entity = (ENTITY *)lookup(&dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!entity) + return XML_ERROR_NO_MEMORY; + entity->base = curBase; + dtd->paramEntityRead = XML_FALSE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + if (dtd->paramEntityRead) { + if (!dtd->standalone && + notStandaloneHandler && + !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; + } + /* if we didn't read the foreign DTD then this means that there + is no external subset and we must reset dtd->hasParamEntityRefs + */ + else + dtd->hasParamEntityRefs = hadParamEntityRefs; + /* end of DTD - no need to update dtd->keepProcessing */ + } + } +#endif /* XML_DTD */ + processor = contentProcessor; + return contentProcessor(parser, s, end, nextPtr); + case XML_ROLE_ATTLIST_ELEMENT_NAME: + declElementType = getElementType(parser, enc, s, next); + if (!declElementType) + return XML_ERROR_NO_MEMORY; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_NAME: + declAttributeId = getAttributeId(parser, enc, s, next); + if (!declAttributeId) + return XML_ERROR_NO_MEMORY; + declAttributeIsCdata = XML_FALSE; + declAttributeType = NULL; + declAttributeIsId = XML_FALSE; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_CDATA: + declAttributeIsCdata = XML_TRUE; + declAttributeType = atypeCDATA; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_ID: + declAttributeIsId = XML_TRUE; + declAttributeType = atypeID; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_IDREF: + declAttributeType = atypeIDREF; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_IDREFS: + declAttributeType = atypeIDREFS; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_ENTITY: + declAttributeType = atypeENTITY; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_ENTITIES: + declAttributeType = atypeENTITIES; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN: + declAttributeType = atypeNMTOKEN; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS: + declAttributeType = atypeNMTOKENS; + checkAttListDeclHandler: + if (dtd->keepProcessing && attlistDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ATTRIBUTE_ENUM_VALUE: + case XML_ROLE_ATTRIBUTE_NOTATION_VALUE: + if (dtd->keepProcessing && attlistDeclHandler) { + const XML_Char *prefix; + if (declAttributeType) { + prefix = enumValueSep; + } + else { + prefix = (role == XML_ROLE_ATTRIBUTE_NOTATION_VALUE + ? notationPrefix + : enumValueStart); + } + if (!poolAppendString(&tempPool, prefix)) + return XML_ERROR_NO_MEMORY; + if (!poolAppend(&tempPool, enc, s, next)) + return XML_ERROR_NO_MEMORY; + declAttributeType = tempPool.start; + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_IMPLIED_ATTRIBUTE_VALUE: + case XML_ROLE_REQUIRED_ATTRIBUTE_VALUE: + if (dtd->keepProcessing) { + if (!defineAttribute(declElementType, declAttributeId, + declAttributeIsCdata, declAttributeIsId, + 0, parser)) + return XML_ERROR_NO_MEMORY; + if (attlistDeclHandler && declAttributeType) { + if (*declAttributeType == XML_T('(') + || (*declAttributeType == XML_T('N') + && declAttributeType[1] == XML_T('O'))) { + /* Enumerated or Notation type */ + if (!poolAppendChar(&tempPool, XML_T(')')) + || !poolAppendChar(&tempPool, XML_T('\0'))) + return XML_ERROR_NO_MEMORY; + declAttributeType = tempPool.start; + poolFinish(&tempPool); + } + *eventEndPP = s; + attlistDeclHandler(handlerArg, declElementType->name, + declAttributeId->name, declAttributeType, + 0, role == XML_ROLE_REQUIRED_ATTRIBUTE_VALUE); + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + } + break; + case XML_ROLE_DEFAULT_ATTRIBUTE_VALUE: + case XML_ROLE_FIXED_ATTRIBUTE_VALUE: + if (dtd->keepProcessing) { + const XML_Char *attVal; + enum XML_Error result = + storeAttributeValue(parser, enc, declAttributeIsCdata, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar, + &dtd->pool); + if (result) + return result; + attVal = poolStart(&dtd->pool); + poolFinish(&dtd->pool); + /* ID attributes aren't allowed to have a default */ + if (!defineAttribute(declElementType, declAttributeId, + declAttributeIsCdata, XML_FALSE, attVal, parser)) + return XML_ERROR_NO_MEMORY; + if (attlistDeclHandler && declAttributeType) { + if (*declAttributeType == XML_T('(') + || (*declAttributeType == XML_T('N') + && declAttributeType[1] == XML_T('O'))) { + /* Enumerated or Notation type */ + if (!poolAppendChar(&tempPool, XML_T(')')) + || !poolAppendChar(&tempPool, XML_T('\0'))) + return XML_ERROR_NO_MEMORY; + declAttributeType = tempPool.start; + poolFinish(&tempPool); + } + *eventEndPP = s; + attlistDeclHandler(handlerArg, declElementType->name, + declAttributeId->name, declAttributeType, + attVal, + role == XML_ROLE_FIXED_ATTRIBUTE_VALUE); + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + } + break; + case XML_ROLE_ENTITY_VALUE: + if (dtd->keepProcessing) { + enum XML_Error result = storeEntityValue(parser, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (declEntity) { + declEntity->textPtr = poolStart(&dtd->entityValuePool); + declEntity->textLen = (int)(poolLength(&dtd->entityValuePool)); + poolFinish(&dtd->entityValuePool); + if (entityDeclHandler) { + *eventEndPP = s; + entityDeclHandler(handlerArg, + declEntity->name, + declEntity->is_param, + declEntity->textPtr, + declEntity->textLen, + curBase, 0, 0, 0); + handleDefault = XML_FALSE; + } + } + else + poolDiscard(&dtd->entityValuePool); + if (result != XML_ERROR_NONE) + return result; + } + break; + case XML_ROLE_DOCTYPE_SYSTEM_ID: +#ifdef XML_DTD + useForeignDTD = XML_FALSE; +#endif /* XML_DTD */ + dtd->hasParamEntityRefs = XML_TRUE; + if (startDoctypeDeclHandler) { + doctypeSysid = poolStoreString(&tempPool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (doctypeSysid == NULL) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + handleDefault = XML_FALSE; + } +#ifdef XML_DTD + else + /* use externalSubsetName to make doctypeSysid non-NULL + for the case where no startDoctypeDeclHandler is set */ + doctypeSysid = externalSubsetName; +#endif /* XML_DTD */ + if (!dtd->standalone +#ifdef XML_DTD + && !paramEntityParsing +#endif /* XML_DTD */ + && notStandaloneHandler + && !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; +#ifndef XML_DTD + break; +#else /* XML_DTD */ + if (!declEntity) { + declEntity = (ENTITY *)lookup(&dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; + declEntity->publicId = NULL; + } + /* fall through */ +#endif /* XML_DTD */ + case XML_ROLE_ENTITY_SYSTEM_ID: + if (dtd->keepProcessing && declEntity) { + declEntity->systemId = poolStoreString(&dtd->pool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!declEntity->systemId) + return XML_ERROR_NO_MEMORY; + declEntity->base = curBase; + poolFinish(&dtd->pool); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_ENTITY_COMPLETE: + if (dtd->keepProcessing && declEntity && entityDeclHandler) { + *eventEndPP = s; + entityDeclHandler(handlerArg, + declEntity->name, + declEntity->is_param, + 0,0, + declEntity->base, + declEntity->systemId, + declEntity->publicId, + 0); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_ENTITY_NOTATION_NAME: + if (dtd->keepProcessing && declEntity) { + declEntity->notation = poolStoreString(&dtd->pool, enc, s, next); + if (!declEntity->notation) + return XML_ERROR_NO_MEMORY; + poolFinish(&dtd->pool); + if (unparsedEntityDeclHandler) { + *eventEndPP = s; + unparsedEntityDeclHandler(handlerArg, + declEntity->name, + declEntity->base, + declEntity->systemId, + declEntity->publicId, + declEntity->notation); + handleDefault = XML_FALSE; + } + else if (entityDeclHandler) { + *eventEndPP = s; + entityDeclHandler(handlerArg, + declEntity->name, + 0,0,0, + declEntity->base, + declEntity->systemId, + declEntity->publicId, + declEntity->notation); + handleDefault = XML_FALSE; + } + } + break; + case XML_ROLE_GENERAL_ENTITY_NAME: + { + if (XmlPredefinedEntityName(enc, s, next)) { + declEntity = NULL; + break; + } + if (dtd->keepProcessing) { + const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next); + if (!name) + return XML_ERROR_NO_MEMORY; + declEntity = (ENTITY *)lookup(&dtd->generalEntities, name, + sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; + if (declEntity->name != name) { + poolDiscard(&dtd->pool); + declEntity = NULL; + } + else { + poolFinish(&dtd->pool); + declEntity->publicId = NULL; + declEntity->is_param = XML_FALSE; + /* if we have a parent parser or are reading an internal parameter + entity, then the entity declaration is not considered "internal" + */ + declEntity->is_internal = !(parentParser || openInternalEntities); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + } + else { + poolDiscard(&dtd->pool); + declEntity = NULL; + } + } + break; + case XML_ROLE_PARAM_ENTITY_NAME: +#ifdef XML_DTD + if (dtd->keepProcessing) { + const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next); + if (!name) + return XML_ERROR_NO_MEMORY; + declEntity = (ENTITY *)lookup(&dtd->paramEntities, + name, sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; + if (declEntity->name != name) { + poolDiscard(&dtd->pool); + declEntity = NULL; + } + else { + poolFinish(&dtd->pool); + declEntity->publicId = NULL; + declEntity->is_param = XML_TRUE; + /* if we have a parent parser or are reading an internal parameter + entity, then the entity declaration is not considered "internal" + */ + declEntity->is_internal = !(parentParser || openInternalEntities); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + } + else { + poolDiscard(&dtd->pool); + declEntity = NULL; + } +#else /* not XML_DTD */ + declEntity = NULL; +#endif /* XML_DTD */ + break; + case XML_ROLE_NOTATION_NAME: + declNotationPublicId = NULL; + declNotationName = NULL; + if (notationDeclHandler) { + declNotationName = poolStoreString(&tempPool, enc, s, next); + if (!declNotationName) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_NOTATION_PUBLIC_ID: + if (!XmlIsPublicId(enc, s, next, eventPP)) + return XML_ERROR_PUBLICID; + if (declNotationName) { /* means notationDeclHandler != NULL */ + XML_Char *tem = poolStoreString(&tempPool, + enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!tem) + return XML_ERROR_NO_MEMORY; + normalizePublicId(tem); + declNotationPublicId = tem; + poolFinish(&tempPool); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_NOTATION_SYSTEM_ID: + if (declNotationName && notationDeclHandler) { + const XML_Char *systemId + = poolStoreString(&tempPool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!systemId) + return XML_ERROR_NO_MEMORY; + *eventEndPP = s; + notationDeclHandler(handlerArg, + declNotationName, + curBase, + systemId, + declNotationPublicId); + handleDefault = XML_FALSE; + } + poolClear(&tempPool); + break; + case XML_ROLE_NOTATION_NO_SYSTEM_ID: + if (declNotationPublicId && notationDeclHandler) { + *eventEndPP = s; + notationDeclHandler(handlerArg, + declNotationName, + curBase, + 0, + declNotationPublicId); + handleDefault = XML_FALSE; + } + poolClear(&tempPool); + break; + case XML_ROLE_ERROR: + switch (tok) { + case XML_TOK_PARAM_ENTITY_REF: + /* PE references in internal subset are + not allowed within declarations. */ + return XML_ERROR_PARAM_ENTITY_REF; + case XML_TOK_XML_DECL: + return XML_ERROR_MISPLACED_XML_PI; + default: + return XML_ERROR_SYNTAX; + } +#ifdef XML_DTD + case XML_ROLE_IGNORE_SECT: + { + enum XML_Error result; + if (defaultHandler) + reportDefault(parser, enc, s, next); + handleDefault = XML_FALSE; + result = doIgnoreSection(parser, enc, &next, end, nextPtr, haveMore); + if (result != XML_ERROR_NONE) + return result; + else if (!next) { + processor = ignoreSectionProcessor; + return result; + } + } + break; +#endif /* XML_DTD */ + case XML_ROLE_GROUP_OPEN: + if (prologState.level >= groupSize) { + if (groupSize) { + char *temp = (char *)REALLOC(groupConnector, groupSize *= 2); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + groupConnector = temp; + if (dtd->scaffIndex) { + int *temp = (int *)REALLOC(dtd->scaffIndex, + groupSize * sizeof(int)); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + dtd->scaffIndex = temp; + } + } + else { + groupConnector = (char *)MALLOC(groupSize = 32); + if (!groupConnector) + return XML_ERROR_NO_MEMORY; + } + } + groupConnector[prologState.level] = 0; + if (dtd->in_eldecl) { + int myindex = nextScaffoldPart(parser); + if (myindex < 0) + return XML_ERROR_NO_MEMORY; + dtd->scaffIndex[dtd->scaffLevel] = myindex; + dtd->scaffLevel++; + dtd->scaffold[myindex].type = XML_CTYPE_SEQ; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_GROUP_SEQUENCE: + if (groupConnector[prologState.level] == '|') + return XML_ERROR_SYNTAX; + groupConnector[prologState.level] = ','; + if (dtd->in_eldecl && elementDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_GROUP_CHOICE: + if (groupConnector[prologState.level] == ',') + return XML_ERROR_SYNTAX; + if (dtd->in_eldecl + && !groupConnector[prologState.level] + && (dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type + != XML_CTYPE_MIXED) + ) { + dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type + = XML_CTYPE_CHOICE; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + groupConnector[prologState.level] = '|'; + break; + case XML_ROLE_PARAM_ENTITY_REF: +#ifdef XML_DTD + case XML_ROLE_INNER_PARAM_ENTITY_REF: + dtd->hasParamEntityRefs = XML_TRUE; + if (!paramEntityParsing) + dtd->keepProcessing = dtd->standalone; + else { + const XML_Char *name; + ENTITY *entity; + name = poolStoreString(&dtd->pool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(&dtd->paramEntities, name, 0); + poolDiscard(&dtd->pool); + /* first, determine if a check for an existing declaration is needed; + if yes, check that the entity exists, and that it is internal, + otherwise call the skipped entity handler + */ + if (prologState.documentEntity && + (dtd->standalone + ? !openInternalEntities + : !dtd->hasParamEntityRefs)) { + if (!entity) + return XML_ERROR_UNDEFINED_ENTITY; + else if (!entity->is_internal) + return XML_ERROR_ENTITY_DECLARED_IN_PE; + } + else if (!entity) { + dtd->keepProcessing = dtd->standalone; + /* cannot report skipped entities in declarations */ + if ((role == XML_ROLE_PARAM_ENTITY_REF) && skippedEntityHandler) { + skippedEntityHandler(handlerArg, name, 1); + handleDefault = XML_FALSE; + } + break; + } + if (entity->open) + return XML_ERROR_RECURSIVE_ENTITY_REF; + if (entity->textPtr) { + enum XML_Error result; + XML_Bool betweenDecl = + (role == XML_ROLE_PARAM_ENTITY_REF ? XML_TRUE : XML_FALSE); + result = processInternalEntity(parser, entity, betweenDecl); + if (result != XML_ERROR_NONE) + return result; + handleDefault = XML_FALSE; + break; + } + if (externalEntityRefHandler) { + dtd->paramEntityRead = XML_FALSE; + entity->open = XML_TRUE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) { + entity->open = XML_FALSE; + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + } + entity->open = XML_FALSE; + handleDefault = XML_FALSE; + if (!dtd->paramEntityRead) { + dtd->keepProcessing = dtd->standalone; + break; + } + } + else { + dtd->keepProcessing = dtd->standalone; + break; + } + } +#endif /* XML_DTD */ + if (!dtd->standalone && + notStandaloneHandler && + !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; + break; + + /* Element declaration stuff */ + + case XML_ROLE_ELEMENT_NAME: + if (elementDeclHandler) { + declElementType = getElementType(parser, enc, s, next); + if (!declElementType) + return XML_ERROR_NO_MEMORY; + dtd->scaffLevel = 0; + dtd->scaffCount = 0; + dtd->in_eldecl = XML_TRUE; + handleDefault = XML_FALSE; + } + break; + + case XML_ROLE_CONTENT_ANY: + case XML_ROLE_CONTENT_EMPTY: + if (dtd->in_eldecl) { + if (elementDeclHandler) { + XML_Content * content = (XML_Content *) MALLOC(sizeof(XML_Content)); + if (!content) + return XML_ERROR_NO_MEMORY; + content->quant = XML_CQUANT_NONE; + content->name = NULL; + content->numchildren = 0; + content->children = NULL; + content->type = ((role == XML_ROLE_CONTENT_ANY) ? + XML_CTYPE_ANY : + XML_CTYPE_EMPTY); + *eventEndPP = s; + elementDeclHandler(handlerArg, declElementType->name, content); + handleDefault = XML_FALSE; + } + dtd->in_eldecl = XML_FALSE; + } + break; + + case XML_ROLE_CONTENT_PCDATA: + if (dtd->in_eldecl) { + dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type + = XML_CTYPE_MIXED; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + break; + + case XML_ROLE_CONTENT_ELEMENT: + quant = XML_CQUANT_NONE; + goto elementContent; + case XML_ROLE_CONTENT_ELEMENT_OPT: + quant = XML_CQUANT_OPT; + goto elementContent; + case XML_ROLE_CONTENT_ELEMENT_REP: + quant = XML_CQUANT_REP; + goto elementContent; + case XML_ROLE_CONTENT_ELEMENT_PLUS: + quant = XML_CQUANT_PLUS; + elementContent: + if (dtd->in_eldecl) { + ELEMENT_TYPE *el; + const XML_Char *name; + int nameLen; + const char *nxt = (quant == XML_CQUANT_NONE + ? next + : next - enc->minBytesPerChar); + int myindex = nextScaffoldPart(parser); + if (myindex < 0) + return XML_ERROR_NO_MEMORY; + dtd->scaffold[myindex].type = XML_CTYPE_NAME; + dtd->scaffold[myindex].quant = quant; + el = getElementType(parser, enc, s, nxt); + if (!el) + return XML_ERROR_NO_MEMORY; + name = el->name; + dtd->scaffold[myindex].name = name; + nameLen = 0; + for (; name[nameLen++]; ); + dtd->contentStringLen += nameLen; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + break; + + case XML_ROLE_GROUP_CLOSE: + quant = XML_CQUANT_NONE; + goto closeGroup; + case XML_ROLE_GROUP_CLOSE_OPT: + quant = XML_CQUANT_OPT; + goto closeGroup; + case XML_ROLE_GROUP_CLOSE_REP: + quant = XML_CQUANT_REP; + goto closeGroup; + case XML_ROLE_GROUP_CLOSE_PLUS: + quant = XML_CQUANT_PLUS; + closeGroup: + if (dtd->in_eldecl) { + if (elementDeclHandler) + handleDefault = XML_FALSE; + dtd->scaffLevel--; + dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel]].quant = quant; + if (dtd->scaffLevel == 0) { + if (!handleDefault) { + XML_Content *model = build_model(parser); + if (!model) + return XML_ERROR_NO_MEMORY; + *eventEndPP = s; + elementDeclHandler(handlerArg, declElementType->name, model); + } + dtd->in_eldecl = XML_FALSE; + dtd->contentStringLen = 0; + } + } + break; + /* End element declaration stuff */ + + case XML_ROLE_PI: + if (!reportProcessingInstruction(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + handleDefault = XML_FALSE; + break; + case XML_ROLE_COMMENT: + if (!reportComment(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + handleDefault = XML_FALSE; + break; + case XML_ROLE_NONE: + switch (tok) { + case XML_TOK_BOM: + handleDefault = XML_FALSE; + break; + } + break; + case XML_ROLE_DOCTYPE_NONE: + if (startDoctypeDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ENTITY_NONE: + if (dtd->keepProcessing && entityDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_NOTATION_NONE: + if (notationDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ATTLIST_NONE: + if (dtd->keepProcessing && attlistDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ELEMENT_NONE: + if (elementDeclHandler) + handleDefault = XML_FALSE; + break; + } /* end of big switch */ + + if (handleDefault && defaultHandler) + reportDefault(parser, enc, s, next); + + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: + s = next; + tok = XmlPrologTok(enc, s, end, &next); + } + } + /* not reached */ +} + +static enum XML_Error PTRCALL +epilogProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + processor = epilogProcessor; + eventPtr = s; + for (;;) { + const char *next = NULL; + int tok = XmlPrologTok(encoding, s, end, &next); + eventEndPtr = next; + switch (tok) { + /* report partial linebreak - it might be the last token */ + case -XML_TOK_PROLOG_S: + if (defaultHandler) { + reportDefault(parser, encoding, s, next); + if (ps_parsing == XML_FINISHED) + return XML_ERROR_ABORTED; + } + *nextPtr = next; + return XML_ERROR_NONE; + case XML_TOK_NONE: + *nextPtr = s; + return XML_ERROR_NONE; + case XML_TOK_PROLOG_S: + if (defaultHandler) + reportDefault(parser, encoding, s, next); + break; + case XML_TOK_PI: + if (!reportProcessingInstruction(parser, encoding, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_COMMENT: + if (!reportComment(parser, encoding, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_INVALID: + eventPtr = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + if (!ps_finalBuffer) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (!ps_finalBuffer) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + default: + return XML_ERROR_JUNK_AFTER_DOC_ELEMENT; + } + eventPtr = s = next; + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: ; + } + } +} + +static enum XML_Error +processInternalEntity(XML_Parser parser, ENTITY *entity, + XML_Bool betweenDecl) +{ + const char *textStart, *textEnd; + const char *next; + enum XML_Error result; + OPEN_INTERNAL_ENTITY *openEntity; + + if (freeInternalEntities) { + openEntity = freeInternalEntities; + freeInternalEntities = openEntity->next; + } + else { + openEntity = (OPEN_INTERNAL_ENTITY *)MALLOC(sizeof(OPEN_INTERNAL_ENTITY)); + if (!openEntity) + return XML_ERROR_NO_MEMORY; + } + entity->open = XML_TRUE; + entity->processed = 0; + openEntity->next = openInternalEntities; + openInternalEntities = openEntity; + openEntity->entity = entity; + openEntity->startTagLevel = tagLevel; + openEntity->betweenDecl = betweenDecl; + openEntity->internalEventPtr = NULL; + openEntity->internalEventEndPtr = NULL; + textStart = (char *)entity->textPtr; + textEnd = (char *)(entity->textPtr + entity->textLen); + +#ifdef XML_DTD + if (entity->is_param) { + int tok = XmlPrologTok(internalEncoding, textStart, textEnd, &next); + result = doProlog(parser, internalEncoding, textStart, textEnd, tok, + next, &next, XML_FALSE); + } + else +#endif /* XML_DTD */ + result = doContent(parser, tagLevel, internalEncoding, textStart, + textEnd, &next, XML_FALSE); + + if (result == XML_ERROR_NONE) { + if (textEnd != next && ps_parsing == XML_SUSPENDED) { + entity->processed = (int)(next - textStart); + processor = internalEntityProcessor; + } + else { + entity->open = XML_FALSE; + openInternalEntities = openEntity->next; + /* put openEntity back in list of free instances */ + openEntity->next = freeInternalEntities; + freeInternalEntities = openEntity; + } + } + return result; +} + +static enum XML_Error PTRCALL +internalEntityProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + ENTITY *entity; + const char *textStart, *textEnd; + const char *next; + enum XML_Error result; + OPEN_INTERNAL_ENTITY *openEntity = openInternalEntities; + if (!openEntity) + return XML_ERROR_UNEXPECTED_STATE; + + entity = openEntity->entity; + textStart = ((char *)entity->textPtr) + entity->processed; + textEnd = (char *)(entity->textPtr + entity->textLen); + +#ifdef XML_DTD + if (entity->is_param) { + int tok = XmlPrologTok(internalEncoding, textStart, textEnd, &next); + result = doProlog(parser, internalEncoding, textStart, textEnd, tok, + next, &next, XML_FALSE); + } + else +#endif /* XML_DTD */ + result = doContent(parser, openEntity->startTagLevel, internalEncoding, + textStart, textEnd, &next, XML_FALSE); + + if (result != XML_ERROR_NONE) + return result; + else if (textEnd != next && ps_parsing == XML_SUSPENDED) { + entity->processed = (int)(next - (char *)entity->textPtr); + return result; + } + else { + entity->open = XML_FALSE; + openInternalEntities = openEntity->next; + /* put openEntity back in list of free instances */ + openEntity->next = freeInternalEntities; + freeInternalEntities = openEntity; + } + +#ifdef XML_DTD + if (entity->is_param) { + int tok; + processor = prologProcessor; + tok = XmlPrologTok(encoding, s, end, &next); + return doProlog(parser, encoding, s, end, tok, next, nextPtr, + (XML_Bool)!ps_finalBuffer); + } + else +#endif /* XML_DTD */ + { + processor = contentProcessor; + /* see externalEntityContentProcessor vs contentProcessor */ + return doContent(parser, parentParser ? 1 : 0, encoding, s, end, + nextPtr, (XML_Bool)!ps_finalBuffer); + } +} + +static enum XML_Error PTRCALL +errorProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + return errorCode; +} + +static enum XML_Error +storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + const char *ptr, const char *end, + STRING_POOL *pool) +{ + enum XML_Error result = appendAttributeValue(parser, enc, isCdata, ptr, + end, pool); + if (result) + return result; + if (!isCdata && poolLength(pool) && poolLastChar(pool) == 0x20) + poolChop(pool); + if (!poolAppendChar(pool, XML_T('\0'))) + return XML_ERROR_NO_MEMORY; + return XML_ERROR_NONE; +} + +static enum XML_Error +appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + const char *ptr, const char *end, + STRING_POOL *pool) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + for (;;) { + const char *next; + int tok = XmlAttributeValueTok(enc, ptr, end, &next); + switch (tok) { + case XML_TOK_NONE: + return XML_ERROR_NONE; + case XML_TOK_INVALID: + if (enc == encoding) + eventPtr = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_CHAR_REF: + { + XML_Char buf[XML_ENCODE_MAX]; + int i; + int n = XmlCharRefNumber(enc, ptr); + if (n < 0) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_BAD_CHAR_REF; + } + if (!isCdata + && n == 0x20 /* space */ + && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20)) + break; + n = XmlEncode(n, (ICHAR *)buf); + if (!n) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_BAD_CHAR_REF; + } + for (i = 0; i < n; i++) { + if (!poolAppendChar(pool, buf[i])) + return XML_ERROR_NO_MEMORY; + } + } + break; + case XML_TOK_DATA_CHARS: + if (!poolAppend(pool, enc, ptr, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_TRAILING_CR: + next = ptr + enc->minBytesPerChar; + /* fall through */ + case XML_TOK_ATTRIBUTE_VALUE_S: + case XML_TOK_DATA_NEWLINE: + if (!isCdata && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20)) + break; + if (!poolAppendChar(pool, 0x20)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_ENTITY_REF: + { + const XML_Char *name; + ENTITY *entity; + char checkEntityDecl; + XML_Char ch = (XML_Char) XmlPredefinedEntityName(enc, + ptr + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (ch) { + if (!poolAppendChar(pool, ch)) + return XML_ERROR_NO_MEMORY; + break; + } + name = poolStoreString(&temp2Pool, enc, + ptr + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(&dtd->generalEntities, name, 0); + poolDiscard(&temp2Pool); + /* First, determine if a check for an existing declaration is needed; + if yes, check that the entity exists, and that it is internal. + */ + if (pool == &dtd->pool) /* are we called from prolog? */ + checkEntityDecl = +#ifdef XML_DTD + prologState.documentEntity && +#endif /* XML_DTD */ + (dtd->standalone + ? !openInternalEntities + : !dtd->hasParamEntityRefs); + else /* if (pool == &tempPool): we are called from content */ + checkEntityDecl = !dtd->hasParamEntityRefs || dtd->standalone; + if (checkEntityDecl) { + if (!entity) + return XML_ERROR_UNDEFINED_ENTITY; + else if (!entity->is_internal) + return XML_ERROR_ENTITY_DECLARED_IN_PE; + } + else if (!entity) { + /* Cannot report skipped entity here - see comments on + skippedEntityHandler. + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, name, 0); + */ + /* Cannot call the default handler because this would be + out of sync with the call to the startElementHandler. + if ((pool == &tempPool) && defaultHandler) + reportDefault(parser, enc, ptr, next); + */ + break; + } + if (entity->open) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_RECURSIVE_ENTITY_REF; + } + if (entity->notation) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_BINARY_ENTITY_REF; + } + if (!entity->textPtr) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF; + } + else { + enum XML_Error result; + const XML_Char *textEnd = entity->textPtr + entity->textLen; + entity->open = XML_TRUE; + result = appendAttributeValue(parser, internalEncoding, isCdata, + (char *)entity->textPtr, + (char *)textEnd, pool); + entity->open = XML_FALSE; + if (result) + return result; + } + } + break; + default: + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_UNEXPECTED_STATE; + } + ptr = next; + } + /* not reached */ +} + +static enum XML_Error +storeEntityValue(XML_Parser parser, + const ENCODING *enc, + const char *entityTextPtr, + const char *entityTextEnd) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + STRING_POOL *pool = &(dtd->entityValuePool); + enum XML_Error result = XML_ERROR_NONE; +#ifdef XML_DTD + int oldInEntityValue = prologState.inEntityValue; + prologState.inEntityValue = 1; +#endif /* XML_DTD */ + /* never return Null for the value argument in EntityDeclHandler, + since this would indicate an external entity; therefore we + have to make sure that entityValuePool.start is not null */ + if (!pool->blocks) { + if (!poolGrow(pool)) + return XML_ERROR_NO_MEMORY; + } + + for (;;) { + const char *next; + int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next); + switch (tok) { + case XML_TOK_PARAM_ENTITY_REF: +#ifdef XML_DTD + if (isParamEntity || enc != encoding) { + const XML_Char *name; + ENTITY *entity; + name = poolStoreString(&tempPool, enc, + entityTextPtr + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + entity = (ENTITY *)lookup(&dtd->paramEntities, name, 0); + poolDiscard(&tempPool); + if (!entity) { + /* not a well-formedness error - see XML 1.0: WFC Entity Declared */ + /* cannot report skipped entity here - see comments on + skippedEntityHandler + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, name, 0); + */ + dtd->keepProcessing = dtd->standalone; + goto endEntityValue; + } + if (entity->open) { + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_RECURSIVE_ENTITY_REF; + goto endEntityValue; + } + if (entity->systemId) { + if (externalEntityRefHandler) { + dtd->paramEntityRead = XML_FALSE; + entity->open = XML_TRUE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) { + entity->open = XML_FALSE; + result = XML_ERROR_EXTERNAL_ENTITY_HANDLING; + goto endEntityValue; + } + entity->open = XML_FALSE; + if (!dtd->paramEntityRead) + dtd->keepProcessing = dtd->standalone; + } + else + dtd->keepProcessing = dtd->standalone; + } + else { + entity->open = XML_TRUE; + result = storeEntityValue(parser, + internalEncoding, + (char *)entity->textPtr, + (char *)(entity->textPtr + + entity->textLen)); + entity->open = XML_FALSE; + if (result) + goto endEntityValue; + } + break; + } +#endif /* XML_DTD */ + /* In the internal subset, PE references are not legal + within markup declarations, e.g entity values in this case. */ + eventPtr = entityTextPtr; + result = XML_ERROR_PARAM_ENTITY_REF; + goto endEntityValue; + case XML_TOK_NONE: + result = XML_ERROR_NONE; + goto endEntityValue; + case XML_TOK_ENTITY_REF: + case XML_TOK_DATA_CHARS: + if (!poolAppend(pool, enc, entityTextPtr, next)) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + break; + case XML_TOK_TRAILING_CR: + next = entityTextPtr + enc->minBytesPerChar; + /* fall through */ + case XML_TOK_DATA_NEWLINE: + if (pool->end == pool->ptr && !poolGrow(pool)) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + *(pool->ptr)++ = 0xA; + break; + case XML_TOK_CHAR_REF: + { + XML_Char buf[XML_ENCODE_MAX]; + int i; + int n = XmlCharRefNumber(enc, entityTextPtr); + if (n < 0) { + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_BAD_CHAR_REF; + goto endEntityValue; + } + n = XmlEncode(n, (ICHAR *)buf); + if (!n) { + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_BAD_CHAR_REF; + goto endEntityValue; + } + for (i = 0; i < n; i++) { + if (pool->end == pool->ptr && !poolGrow(pool)) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + *(pool->ptr)++ = buf[i]; + } + } + break; + case XML_TOK_PARTIAL: + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_INVALID_TOKEN; + goto endEntityValue; + case XML_TOK_INVALID: + if (enc == encoding) + eventPtr = next; + result = XML_ERROR_INVALID_TOKEN; + goto endEntityValue; + default: + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_UNEXPECTED_STATE; + goto endEntityValue; + } + entityTextPtr = next; + } +endEntityValue: +#ifdef XML_DTD + prologState.inEntityValue = oldInEntityValue; +#endif /* XML_DTD */ + return result; +} + +static void FASTCALL +normalizeLines(XML_Char *s) +{ + XML_Char *p; + for (;; s++) { + if (*s == XML_T('\0')) + return; + if (*s == 0xD) + break; + } + p = s; + do { + if (*s == 0xD) { + *p++ = 0xA; + if (*++s == 0xA) + s++; + } + else + *p++ = *s++; + } while (*s); + *p = XML_T('\0'); +} + +static int +reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end) +{ + const XML_Char *target; + XML_Char *data; + const char *tem; + if (!processingInstructionHandler) { + if (defaultHandler) + reportDefault(parser, enc, start, end); + return 1; + } + start += enc->minBytesPerChar * 2; + tem = start + XmlNameLength(enc, start); + target = poolStoreString(&tempPool, enc, start, tem); + if (!target) + return 0; + poolFinish(&tempPool); + data = poolStoreString(&tempPool, enc, + XmlSkipS(enc, tem), + end - enc->minBytesPerChar*2); + if (!data) + return 0; + normalizeLines(data); + processingInstructionHandler(handlerArg, target, data); + poolClear(&tempPool); + return 1; +} + +static int +reportComment(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end) +{ + XML_Char *data; + if (!commentHandler) { + if (defaultHandler) + reportDefault(parser, enc, start, end); + return 1; + } + data = poolStoreString(&tempPool, + enc, + start + enc->minBytesPerChar * 4, + end - enc->minBytesPerChar * 3); + if (!data) + return 0; + normalizeLines(data); + commentHandler(handlerArg, data); + poolClear(&tempPool); + return 1; +} + +static void +reportDefault(XML_Parser parser, const ENCODING *enc, + const char *s, const char *end) +{ + if (MUST_CONVERT(enc, s)) { + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + do { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd); + *eventEndPP = s; + defaultHandler(handlerArg, dataBuf, (int)(dataPtr - (ICHAR *)dataBuf)); + *eventPP = s; + } while (s != end); + } + else + defaultHandler(handlerArg, (XML_Char *)s, (int)((XML_Char *)end - (XML_Char *)s)); +} + + +static int +defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata, + XML_Bool isId, const XML_Char *value, XML_Parser parser) +{ + DEFAULT_ATTRIBUTE *att; + if (value || isId) { + /* The handling of default attributes gets messed up if we have + a default which duplicates a non-default. */ + int i; + for (i = 0; i < type->nDefaultAtts; i++) + if (attId == type->defaultAtts[i].id) + return 1; + if (isId && !type->idAtt && !attId->xmlns) + type->idAtt = attId; + } + if (type->nDefaultAtts == type->allocDefaultAtts) { + if (type->allocDefaultAtts == 0) { + type->allocDefaultAtts = 8; + type->defaultAtts = (DEFAULT_ATTRIBUTE *)MALLOC(type->allocDefaultAtts + * sizeof(DEFAULT_ATTRIBUTE)); + if (!type->defaultAtts) + return 0; + } + else { + DEFAULT_ATTRIBUTE *temp; + int count = type->allocDefaultAtts * 2; + temp = (DEFAULT_ATTRIBUTE *) + REALLOC(type->defaultAtts, (count * sizeof(DEFAULT_ATTRIBUTE))); + if (temp == NULL) + return 0; + type->allocDefaultAtts = count; + type->defaultAtts = temp; + } + } + att = type->defaultAtts + type->nDefaultAtts; + att->id = attId; + att->value = value; + att->isCdata = isCdata; + if (!isCdata) + attId->maybeTokenized = XML_TRUE; + type->nDefaultAtts += 1; + return 1; +} + +static int +setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *elementType) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + const XML_Char *name; + for (name = elementType->name; *name; name++) { + if (*name == XML_T(':')) { + PREFIX *prefix; + const XML_Char *s; + for (s = elementType->name; s != name; s++) { + if (!poolAppendChar(&dtd->pool, *s)) + return 0; + } + if (!poolAppendChar(&dtd->pool, XML_T('\0'))) + return 0; + prefix = (PREFIX *)lookup(&dtd->prefixes, poolStart(&dtd->pool), + sizeof(PREFIX)); + if (!prefix) + return 0; + if (prefix->name == poolStart(&dtd->pool)) + poolFinish(&dtd->pool); + else + poolDiscard(&dtd->pool); + elementType->prefix = prefix; + + } + } + return 1; +} + +static ATTRIBUTE_ID * +getAttributeId(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + ATTRIBUTE_ID *id; + const XML_Char *name; + if (!poolAppendChar(&dtd->pool, XML_T('\0'))) + return NULL; + name = poolStoreString(&dtd->pool, enc, start, end); + if (!name) + return NULL; + /* skip quotation mark - its storage will be re-used (like in name[-1]) */ + ++name; + id = (ATTRIBUTE_ID *)lookup(&dtd->attributeIds, name, sizeof(ATTRIBUTE_ID)); + if (!id) + return NULL; + if (id->name != name) + poolDiscard(&dtd->pool); + else { + poolFinish(&dtd->pool); + if (!ns) + ; + else if (name[0] == XML_T('x') + && name[1] == XML_T('m') + && name[2] == XML_T('l') + && name[3] == XML_T('n') + && name[4] == XML_T('s') + && (name[5] == XML_T('\0') || name[5] == XML_T(':'))) { + if (name[5] == XML_T('\0')) + id->prefix = &dtd->defaultPrefix; + else + id->prefix = (PREFIX *)lookup(&dtd->prefixes, name + 6, sizeof(PREFIX)); + id->xmlns = XML_TRUE; + } + else { + int i; + for (i = 0; name[i]; i++) { + /* attributes without prefix are *not* in the default namespace */ + if (name[i] == XML_T(':')) { + int j; + for (j = 0; j < i; j++) { + if (!poolAppendChar(&dtd->pool, name[j])) + return NULL; + } + if (!poolAppendChar(&dtd->pool, XML_T('\0'))) + return NULL; + id->prefix = (PREFIX *)lookup(&dtd->prefixes, poolStart(&dtd->pool), + sizeof(PREFIX)); + if (id->prefix->name == poolStart(&dtd->pool)) + poolFinish(&dtd->pool); + else + poolDiscard(&dtd->pool); + break; + } + } + } + } + return id; +} + +#define CONTEXT_SEP XML_T('\f') + +static const XML_Char * +getContext(XML_Parser parser) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + HASH_TABLE_ITER iter; + XML_Bool needSep = XML_FALSE; + + if (dtd->defaultPrefix.binding) { + int i; + int len; + if (!poolAppendChar(&tempPool, XML_T('='))) + return NULL; + len = dtd->defaultPrefix.binding->uriLen; + if (namespaceSeparator) + len--; + for (i = 0; i < len; i++) + if (!poolAppendChar(&tempPool, dtd->defaultPrefix.binding->uri[i])) + return NULL; + needSep = XML_TRUE; + } + + hashTableIterInit(&iter, &(dtd->prefixes)); + for (;;) { + int i; + int len; + const XML_Char *s; + PREFIX *prefix = (PREFIX *)hashTableIterNext(&iter); + if (!prefix) + break; + if (!prefix->binding) + continue; + if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP)) + return NULL; + for (s = prefix->name; *s; s++) + if (!poolAppendChar(&tempPool, *s)) + return NULL; + if (!poolAppendChar(&tempPool, XML_T('='))) + return NULL; + len = prefix->binding->uriLen; + if (namespaceSeparator) + len--; + for (i = 0; i < len; i++) + if (!poolAppendChar(&tempPool, prefix->binding->uri[i])) + return NULL; + needSep = XML_TRUE; + } + + + hashTableIterInit(&iter, &(dtd->generalEntities)); + for (;;) { + const XML_Char *s; + ENTITY *e = (ENTITY *)hashTableIterNext(&iter); + if (!e) + break; + if (!e->open) + continue; + if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP)) + return NULL; + for (s = e->name; *s; s++) + if (!poolAppendChar(&tempPool, *s)) + return 0; + needSep = XML_TRUE; + } + + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return NULL; + return tempPool.start; +} + +static XML_Bool +setContext(XML_Parser parser, const XML_Char *context) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + const XML_Char *s = context; + + while (*context != XML_T('\0')) { + if (*s == CONTEXT_SEP || *s == XML_T('\0')) { + ENTITY *e; + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return XML_FALSE; + e = (ENTITY *)lookup(&dtd->generalEntities, poolStart(&tempPool), 0); + if (e) + e->open = XML_TRUE; + if (*s != XML_T('\0')) + s++; + context = s; + poolDiscard(&tempPool); + } + else if (*s == XML_T('=')) { + PREFIX *prefix; + if (poolLength(&tempPool) == 0) + prefix = &dtd->defaultPrefix; + else { + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return XML_FALSE; + prefix = (PREFIX *)lookup(&dtd->prefixes, poolStart(&tempPool), + sizeof(PREFIX)); + if (!prefix) + return XML_FALSE; + if (prefix->name == poolStart(&tempPool)) { + prefix->name = poolCopyString(&dtd->pool, prefix->name); + if (!prefix->name) + return XML_FALSE; + } + poolDiscard(&tempPool); + } + for (context = s + 1; + *context != CONTEXT_SEP && *context != XML_T('\0'); + context++) + if (!poolAppendChar(&tempPool, *context)) + return XML_FALSE; + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return XML_FALSE; + if (addBinding(parser, prefix, NULL, poolStart(&tempPool), + &inheritedBindings) != XML_ERROR_NONE) + return XML_FALSE; + poolDiscard(&tempPool); + if (*context != XML_T('\0')) + ++context; + s = context; + } + else { + if (!poolAppendChar(&tempPool, *s)) + return XML_FALSE; + s++; + } + } + return XML_TRUE; +} + +static void FASTCALL +normalizePublicId(XML_Char *publicId) +{ + XML_Char *p = publicId; + XML_Char *s; + for (s = publicId; *s; s++) { + switch (*s) { + case 0x20: + case 0xD: + case 0xA: + if (p != publicId && p[-1] != 0x20) + *p++ = 0x20; + break; + default: + *p++ = *s; + } + } + if (p != publicId && p[-1] == 0x20) + --p; + *p = XML_T('\0'); +} + +static DTD * +dtdCreate(const XML_Memory_Handling_Suite *ms) +{ + DTD *p = (DTD *)ms->malloc_fcn(sizeof(DTD)); + if (p == NULL) + return p; + poolInit(&(p->pool), ms); + poolInit(&(p->entityValuePool), ms); + hashTableInit(&(p->generalEntities), ms); + hashTableInit(&(p->elementTypes), ms); + hashTableInit(&(p->attributeIds), ms); + hashTableInit(&(p->prefixes), ms); +#ifdef XML_DTD + p->paramEntityRead = XML_FALSE; + hashTableInit(&(p->paramEntities), ms); +#endif /* XML_DTD */ + p->defaultPrefix.name = NULL; + p->defaultPrefix.binding = NULL; + + p->in_eldecl = XML_FALSE; + p->scaffIndex = NULL; + p->scaffold = NULL; + p->scaffLevel = 0; + p->scaffSize = 0; + p->scaffCount = 0; + p->contentStringLen = 0; + + p->keepProcessing = XML_TRUE; + p->hasParamEntityRefs = XML_FALSE; + p->standalone = XML_FALSE; + return p; +} + +static void +dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) +{ + HASH_TABLE_ITER iter; + hashTableIterInit(&iter, &(p->elementTypes)); + for (;;) { + ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); + if (!e) + break; + if (e->allocDefaultAtts != 0) + ms->free_fcn(e->defaultAtts); + } + hashTableClear(&(p->generalEntities)); +#ifdef XML_DTD + p->paramEntityRead = XML_FALSE; + hashTableClear(&(p->paramEntities)); +#endif /* XML_DTD */ + hashTableClear(&(p->elementTypes)); + hashTableClear(&(p->attributeIds)); + hashTableClear(&(p->prefixes)); + poolClear(&(p->pool)); + poolClear(&(p->entityValuePool)); + p->defaultPrefix.name = NULL; + p->defaultPrefix.binding = NULL; + + p->in_eldecl = XML_FALSE; + + ms->free_fcn(p->scaffIndex); + p->scaffIndex = NULL; + ms->free_fcn(p->scaffold); + p->scaffold = NULL; + + p->scaffLevel = 0; + p->scaffSize = 0; + p->scaffCount = 0; + p->contentStringLen = 0; + + p->keepProcessing = XML_TRUE; + p->hasParamEntityRefs = XML_FALSE; + p->standalone = XML_FALSE; +} + +static void +dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) +{ + HASH_TABLE_ITER iter; + hashTableIterInit(&iter, &(p->elementTypes)); + for (;;) { + ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); + if (!e) + break; + if (e->allocDefaultAtts != 0) + ms->free_fcn(e->defaultAtts); + } + hashTableDestroy(&(p->generalEntities)); +#ifdef XML_DTD + hashTableDestroy(&(p->paramEntities)); +#endif /* XML_DTD */ + hashTableDestroy(&(p->elementTypes)); + hashTableDestroy(&(p->attributeIds)); + hashTableDestroy(&(p->prefixes)); + poolDestroy(&(p->pool)); + poolDestroy(&(p->entityValuePool)); + if (isDocEntity) { + ms->free_fcn(p->scaffIndex); + ms->free_fcn(p->scaffold); + } + ms->free_fcn(p); +} + +/* Do a deep copy of the DTD. Return 0 for out of memory, non-zero otherwise. + The new DTD has already been initialized. +*/ +static int +dtdCopy(DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms) +{ + HASH_TABLE_ITER iter; + + /* Copy the prefix table. */ + + hashTableIterInit(&iter, &(oldDtd->prefixes)); + for (;;) { + const XML_Char *name; + const PREFIX *oldP = (PREFIX *)hashTableIterNext(&iter); + if (!oldP) + break; + name = poolCopyString(&(newDtd->pool), oldP->name); + if (!name) + return 0; + if (!lookup(&(newDtd->prefixes), name, sizeof(PREFIX))) + return 0; + } + + hashTableIterInit(&iter, &(oldDtd->attributeIds)); + + /* Copy the attribute id table. */ + + for (;;) { + ATTRIBUTE_ID *newA; + const XML_Char *name; + const ATTRIBUTE_ID *oldA = (ATTRIBUTE_ID *)hashTableIterNext(&iter); + + if (!oldA) + break; + /* Remember to allocate the scratch byte before the name. */ + if (!poolAppendChar(&(newDtd->pool), XML_T('\0'))) + return 0; + name = poolCopyString(&(newDtd->pool), oldA->name); + if (!name) + return 0; + ++name; + newA = (ATTRIBUTE_ID *)lookup(&(newDtd->attributeIds), name, + sizeof(ATTRIBUTE_ID)); + if (!newA) + return 0; + newA->maybeTokenized = oldA->maybeTokenized; + if (oldA->prefix) { + newA->xmlns = oldA->xmlns; + if (oldA->prefix == &oldDtd->defaultPrefix) + newA->prefix = &newDtd->defaultPrefix; + else + newA->prefix = (PREFIX *)lookup(&(newDtd->prefixes), + oldA->prefix->name, 0); + } + } + + /* Copy the element type table. */ + + hashTableIterInit(&iter, &(oldDtd->elementTypes)); + + for (;;) { + int i; + ELEMENT_TYPE *newE; + const XML_Char *name; + const ELEMENT_TYPE *oldE = (ELEMENT_TYPE *)hashTableIterNext(&iter); + if (!oldE) + break; + name = poolCopyString(&(newDtd->pool), oldE->name); + if (!name) + return 0; + newE = (ELEMENT_TYPE *)lookup(&(newDtd->elementTypes), name, + sizeof(ELEMENT_TYPE)); + if (!newE) + return 0; + if (oldE->nDefaultAtts) { + newE->defaultAtts = (DEFAULT_ATTRIBUTE *) + ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); + if (!newE->defaultAtts) { + ms->free_fcn(newE); + return 0; + } + } + if (oldE->idAtt) + newE->idAtt = (ATTRIBUTE_ID *) + lookup(&(newDtd->attributeIds), oldE->idAtt->name, 0); + newE->allocDefaultAtts = newE->nDefaultAtts = oldE->nDefaultAtts; + if (oldE->prefix) + newE->prefix = (PREFIX *)lookup(&(newDtd->prefixes), + oldE->prefix->name, 0); + for (i = 0; i < newE->nDefaultAtts; i++) { + newE->defaultAtts[i].id = (ATTRIBUTE_ID *) + lookup(&(newDtd->attributeIds), oldE->defaultAtts[i].id->name, 0); + newE->defaultAtts[i].isCdata = oldE->defaultAtts[i].isCdata; + if (oldE->defaultAtts[i].value) { + newE->defaultAtts[i].value + = poolCopyString(&(newDtd->pool), oldE->defaultAtts[i].value); + if (!newE->defaultAtts[i].value) + return 0; + } + else + newE->defaultAtts[i].value = NULL; + } + } + + /* Copy the entity tables. */ + if (!copyEntityTable(&(newDtd->generalEntities), + &(newDtd->pool), + &(oldDtd->generalEntities))) + return 0; + +#ifdef XML_DTD + if (!copyEntityTable(&(newDtd->paramEntities), + &(newDtd->pool), + &(oldDtd->paramEntities))) + return 0; + newDtd->paramEntityRead = oldDtd->paramEntityRead; +#endif /* XML_DTD */ + + newDtd->keepProcessing = oldDtd->keepProcessing; + newDtd->hasParamEntityRefs = oldDtd->hasParamEntityRefs; + newDtd->standalone = oldDtd->standalone; + + /* Don't want deep copying for scaffolding */ + newDtd->in_eldecl = oldDtd->in_eldecl; + newDtd->scaffold = oldDtd->scaffold; + newDtd->contentStringLen = oldDtd->contentStringLen; + newDtd->scaffSize = oldDtd->scaffSize; + newDtd->scaffLevel = oldDtd->scaffLevel; + newDtd->scaffIndex = oldDtd->scaffIndex; + + return 1; +} /* End dtdCopy */ + +static int +copyEntityTable(HASH_TABLE *newTable, + STRING_POOL *newPool, + const HASH_TABLE *oldTable) +{ + HASH_TABLE_ITER iter; + const XML_Char *cachedOldBase = NULL; + const XML_Char *cachedNewBase = NULL; + + hashTableIterInit(&iter, oldTable); + + for (;;) { + ENTITY *newE; + const XML_Char *name; + const ENTITY *oldE = (ENTITY *)hashTableIterNext(&iter); + if (!oldE) + break; + name = poolCopyString(newPool, oldE->name); + if (!name) + return 0; + newE = (ENTITY *)lookup(newTable, name, sizeof(ENTITY)); + if (!newE) + return 0; + if (oldE->systemId) { + const XML_Char *tem = poolCopyString(newPool, oldE->systemId); + if (!tem) + return 0; + newE->systemId = tem; + if (oldE->base) { + if (oldE->base == cachedOldBase) + newE->base = cachedNewBase; + else { + cachedOldBase = oldE->base; + tem = poolCopyString(newPool, cachedOldBase); + if (!tem) + return 0; + cachedNewBase = newE->base = tem; + } + } + if (oldE->publicId) { + tem = poolCopyString(newPool, oldE->publicId); + if (!tem) + return 0; + newE->publicId = tem; + } + } + else { + const XML_Char *tem = poolCopyStringN(newPool, oldE->textPtr, + oldE->textLen); + if (!tem) + return 0; + newE->textPtr = tem; + newE->textLen = oldE->textLen; + } + if (oldE->notation) { + const XML_Char *tem = poolCopyString(newPool, oldE->notation); + if (!tem) + return 0; + newE->notation = tem; + } + newE->is_param = oldE->is_param; + newE->is_internal = oldE->is_internal; + } + return 1; +} + +#define INIT_POWER 6 + +static XML_Bool FASTCALL +keyeq(KEY s1, KEY s2) +{ + for (; *s1 == *s2; s1++, s2++) + if (*s1 == 0) + return XML_TRUE; + return XML_FALSE; +} + +static unsigned long FASTCALL +hash(KEY s) +{ + unsigned long h = 0; + while (*s) + h = CHAR_HASH(h, *s++); + return h; +} + +static NAMED * +lookup(HASH_TABLE *table, KEY name, size_t createSize) +{ + size_t i; + if (table->size == 0) { + size_t tsize; + if (!createSize) + return NULL; + table->power = INIT_POWER; + /* table->size is a power of 2 */ + table->size = (size_t)1 << INIT_POWER; + tsize = table->size * sizeof(NAMED *); + table->v = (NAMED **)table->mem->malloc_fcn(tsize); + if (!table->v) { + table->size = 0; + return NULL; + } + memset(table->v, 0, tsize); + i = hash(name) & ((unsigned long)table->size - 1); + } + else { + unsigned long h = hash(name); + unsigned long mask = (unsigned long)table->size - 1; + unsigned char step = 0; + i = h & mask; + while (table->v[i]) { + if (keyeq(name, table->v[i]->name)) + return table->v[i]; + if (!step) + step = PROBE_STEP(h, mask, table->power); + i < step ? (i += table->size - step) : (i -= step); + } + if (!createSize) + return NULL; + + /* check for overflow (table is half full) */ + if (table->used >> (table->power - 1)) { + unsigned char newPower = table->power + 1; + size_t newSize = (size_t)1 << newPower; + unsigned long newMask = (unsigned long)newSize - 1; + size_t tsize = newSize * sizeof(NAMED *); + NAMED **newV = (NAMED **)table->mem->malloc_fcn(tsize); + if (!newV) + return NULL; + memset(newV, 0, tsize); + for (i = 0; i < table->size; i++) + if (table->v[i]) { + unsigned long newHash = hash(table->v[i]->name); + size_t j = newHash & newMask; + step = 0; + while (newV[j]) { + if (!step) + step = PROBE_STEP(newHash, newMask, newPower); + j < step ? (j += newSize - step) : (j -= step); + } + newV[j] = table->v[i]; + } + table->mem->free_fcn(table->v); + table->v = newV; + table->power = newPower; + table->size = newSize; + i = h & newMask; + step = 0; + while (table->v[i]) { + if (!step) + step = PROBE_STEP(h, newMask, newPower); + i < step ? (i += newSize - step) : (i -= step); + } + } + } + table->v[i] = (NAMED *)table->mem->malloc_fcn(createSize); + if (!table->v[i]) + return NULL; + memset(table->v[i], 0, createSize); + table->v[i]->name = name; + (table->used)++; + return table->v[i]; +} + +static void FASTCALL +hashTableClear(HASH_TABLE *table) +{ + size_t i; + for (i = 0; i < table->size; i++) { + table->mem->free_fcn(table->v[i]); + table->v[i] = NULL; + } + table->used = 0; +} + +static void FASTCALL +hashTableDestroy(HASH_TABLE *table) +{ + size_t i; + for (i = 0; i < table->size; i++) + table->mem->free_fcn(table->v[i]); + table->mem->free_fcn(table->v); +} + +static void FASTCALL +hashTableInit(HASH_TABLE *p, const XML_Memory_Handling_Suite *ms) +{ + p->power = 0; + p->size = 0; + p->used = 0; + p->v = NULL; + p->mem = ms; +} + +static void FASTCALL +hashTableIterInit(HASH_TABLE_ITER *iter, const HASH_TABLE *table) +{ + iter->p = table->v; + iter->end = iter->p + table->size; +} + +static NAMED * FASTCALL +hashTableIterNext(HASH_TABLE_ITER *iter) +{ + while (iter->p != iter->end) { + NAMED *tem = *(iter->p)++; + if (tem) + return tem; + } + return NULL; +} + +static void FASTCALL +poolInit(STRING_POOL *pool, const XML_Memory_Handling_Suite *ms) +{ + pool->blocks = NULL; + pool->freeBlocks = NULL; + pool->start = NULL; + pool->ptr = NULL; + pool->end = NULL; + pool->mem = ms; +} + +static void FASTCALL +poolClear(STRING_POOL *pool) +{ + if (!pool->freeBlocks) + pool->freeBlocks = pool->blocks; + else { + BLOCK *p = pool->blocks; + while (p) { + BLOCK *tem = p->next; + p->next = pool->freeBlocks; + pool->freeBlocks = p; + p = tem; + } + } + pool->blocks = NULL; + pool->start = NULL; + pool->ptr = NULL; + pool->end = NULL; +} + +static void FASTCALL +poolDestroy(STRING_POOL *pool) +{ + BLOCK *p = pool->blocks; + while (p) { + BLOCK *tem = p->next; + pool->mem->free_fcn(p); + p = tem; + } + p = pool->freeBlocks; + while (p) { + BLOCK *tem = p->next; + pool->mem->free_fcn(p); + p = tem; + } +} + +static XML_Char * +poolAppend(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end) +{ + if (!pool->ptr && !poolGrow(pool)) + return NULL; + for (;;) { + XmlConvert(enc, &ptr, end, (ICHAR **)&(pool->ptr), (ICHAR *)pool->end); + if (ptr == end) + break; + if (!poolGrow(pool)) + return NULL; + } + return pool->start; +} + +static const XML_Char * FASTCALL +poolCopyString(STRING_POOL *pool, const XML_Char *s) +{ + do { + if (!poolAppendChar(pool, *s)) + return NULL; + } while (*s++); + s = pool->start; + poolFinish(pool); + return s; +} + +static const XML_Char * +poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n) +{ + if (!pool->ptr && !poolGrow(pool)) + return NULL; + for (; n > 0; --n, s++) { + if (!poolAppendChar(pool, *s)) + return NULL; + } + s = pool->start; + poolFinish(pool); + return s; +} + +static const XML_Char * FASTCALL +poolAppendString(STRING_POOL *pool, const XML_Char *s) +{ + while (*s) { + if (!poolAppendChar(pool, *s)) + return NULL; + s++; + } + return pool->start; +} + +static XML_Char * +poolStoreString(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end) +{ + if (!poolAppend(pool, enc, ptr, end)) + return NULL; + if (pool->ptr == pool->end && !poolGrow(pool)) + return NULL; + *(pool->ptr)++ = 0; + return pool->start; +} + +static XML_Bool FASTCALL +poolGrow(STRING_POOL *pool) +{ + if (pool->freeBlocks) { + if (pool->start == 0) { + pool->blocks = pool->freeBlocks; + pool->freeBlocks = pool->freeBlocks->next; + pool->blocks->next = NULL; + pool->start = pool->blocks->s; + pool->end = pool->start + pool->blocks->size; + pool->ptr = pool->start; + return XML_TRUE; + } + if (pool->end - pool->start < pool->freeBlocks->size) { + BLOCK *tem = pool->freeBlocks->next; + pool->freeBlocks->next = pool->blocks; + pool->blocks = pool->freeBlocks; + pool->freeBlocks = tem; + memcpy(pool->blocks->s, pool->start, + (pool->end - pool->start) * sizeof(XML_Char)); + pool->ptr = pool->blocks->s + (pool->ptr - pool->start); + pool->start = pool->blocks->s; + pool->end = pool->start + pool->blocks->size; + return XML_TRUE; + } + } + if (pool->blocks && pool->start == pool->blocks->s) { + int blockSize = (int)(pool->end - pool->start)*2; + pool->blocks = (BLOCK *) + pool->mem->realloc_fcn(pool->blocks, + (offsetof(BLOCK, s) + + blockSize * sizeof(XML_Char))); + if (pool->blocks == NULL) + return XML_FALSE; + pool->blocks->size = blockSize; + pool->ptr = pool->blocks->s + (pool->ptr - pool->start); + pool->start = pool->blocks->s; + pool->end = pool->start + blockSize; + } + else { + BLOCK *tem; + int blockSize = (int)(pool->end - pool->start); + if (blockSize < INIT_BLOCK_SIZE) + blockSize = INIT_BLOCK_SIZE; + else + blockSize *= 2; + tem = (BLOCK *)pool->mem->malloc_fcn(offsetof(BLOCK, s) + + blockSize * sizeof(XML_Char)); + if (!tem) + return XML_FALSE; + tem->size = blockSize; + tem->next = pool->blocks; + pool->blocks = tem; + if (pool->ptr != pool->start) + memcpy(tem->s, pool->start, + (pool->ptr - pool->start) * sizeof(XML_Char)); + pool->ptr = tem->s + (pool->ptr - pool->start); + pool->start = tem->s; + pool->end = tem->s + blockSize; + } + return XML_TRUE; +} + +static int FASTCALL +nextScaffoldPart(XML_Parser parser) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + CONTENT_SCAFFOLD * me; + int next; + + if (!dtd->scaffIndex) { + dtd->scaffIndex = (int *)MALLOC(groupSize * sizeof(int)); + if (!dtd->scaffIndex) + return -1; + dtd->scaffIndex[0] = 0; + } + + if (dtd->scaffCount >= dtd->scaffSize) { + CONTENT_SCAFFOLD *temp; + if (dtd->scaffold) { + temp = (CONTENT_SCAFFOLD *) + REALLOC(dtd->scaffold, dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD)); + if (temp == NULL) + return -1; + dtd->scaffSize *= 2; + } + else { + temp = (CONTENT_SCAFFOLD *)MALLOC(INIT_SCAFFOLD_ELEMENTS + * sizeof(CONTENT_SCAFFOLD)); + if (temp == NULL) + return -1; + dtd->scaffSize = INIT_SCAFFOLD_ELEMENTS; + } + dtd->scaffold = temp; + } + next = dtd->scaffCount++; + me = &dtd->scaffold[next]; + if (dtd->scaffLevel) { + CONTENT_SCAFFOLD *parent = &dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel-1]]; + if (parent->lastchild) { + dtd->scaffold[parent->lastchild].nextsib = next; + } + if (!parent->childcnt) + parent->firstchild = next; + parent->lastchild = next; + parent->childcnt++; + } + me->firstchild = me->lastchild = me->childcnt = me->nextsib = 0; + return next; +} + +static void +build_node(XML_Parser parser, + int src_node, + XML_Content *dest, + XML_Content **contpos, + XML_Char **strpos) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + dest->type = dtd->scaffold[src_node].type; + dest->quant = dtd->scaffold[src_node].quant; + if (dest->type == XML_CTYPE_NAME) { + const XML_Char *src; + dest->name = *strpos; + src = dtd->scaffold[src_node].name; + for (;;) { + *(*strpos)++ = *src; + if (!*src) + break; + src++; + } + dest->numchildren = 0; + dest->children = NULL; + } + else { + unsigned int i; + int cn; + dest->numchildren = dtd->scaffold[src_node].childcnt; + dest->children = *contpos; + *contpos += dest->numchildren; + for (i = 0, cn = dtd->scaffold[src_node].firstchild; + i < dest->numchildren; + i++, cn = dtd->scaffold[cn].nextsib) { + build_node(parser, cn, &(dest->children[i]), contpos, strpos); + } + dest->name = NULL; + } +} + +static XML_Content * +build_model (XML_Parser parser) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + XML_Content *ret; + XML_Content *cpos; + XML_Char * str; + int allocsize = (dtd->scaffCount * sizeof(XML_Content) + + (dtd->contentStringLen * sizeof(XML_Char))); + + ret = (XML_Content *)MALLOC(allocsize); + if (!ret) + return NULL; + + str = (XML_Char *) (&ret[dtd->scaffCount]); + cpos = &ret[1]; + + build_node(parser, 0, ret, &cpos, &str); + return ret; +} + +static ELEMENT_TYPE * +getElementType(XML_Parser parser, + const ENCODING *enc, + const char *ptr, + const char *end) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + const XML_Char *name = poolStoreString(&dtd->pool, enc, ptr, end); + ELEMENT_TYPE *ret; + + if (!name) + return NULL; + ret = (ELEMENT_TYPE *) lookup(&dtd->elementTypes, name, sizeof(ELEMENT_TYPE)); + if (!ret) + return NULL; + if (ret->name != name) + poolDiscard(&dtd->pool); + else { + poolFinish(&dtd->pool); + if (!setElementTypePrefix(parser, ret)) + return NULL; + } + return ret; +} diff --git a/src/lib/expat/xmlrole.c b/src/lib/expat/xmlrole.c new file mode 100644 index 00000000000..b0aff272432 --- /dev/null +++ b/src/lib/expat/xmlrole.c @@ -0,0 +1,1320 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#include <stddef.h> + +#include "intconfig.h" + +#include "expat_external.h" +#include "internal.h" +#include "xmlrole.h" +#include "ascii.h" + +/* Doesn't check: + + that ,| are not mixed in a model group + content of literals + +*/ + +static const char KW_ANY[] = { + ASCII_A, ASCII_N, ASCII_Y, '\0' }; +static const char KW_ATTLIST[] = { + ASCII_A, ASCII_T, ASCII_T, ASCII_L, ASCII_I, ASCII_S, ASCII_T, '\0' }; +static const char KW_CDATA[] = { + ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; +static const char KW_DOCTYPE[] = { + ASCII_D, ASCII_O, ASCII_C, ASCII_T, ASCII_Y, ASCII_P, ASCII_E, '\0' }; +static const char KW_ELEMENT[] = { + ASCII_E, ASCII_L, ASCII_E, ASCII_M, ASCII_E, ASCII_N, ASCII_T, '\0' }; +static const char KW_EMPTY[] = { + ASCII_E, ASCII_M, ASCII_P, ASCII_T, ASCII_Y, '\0' }; +static const char KW_ENTITIES[] = { + ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_I, ASCII_E, ASCII_S, + '\0' }; +static const char KW_ENTITY[] = { + ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_Y, '\0' }; +static const char KW_FIXED[] = { + ASCII_F, ASCII_I, ASCII_X, ASCII_E, ASCII_D, '\0' }; +static const char KW_ID[] = { + ASCII_I, ASCII_D, '\0' }; +static const char KW_IDREF[] = { + ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, '\0' }; +static const char KW_IDREFS[] = { + ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, ASCII_S, '\0' }; +static const char KW_IGNORE[] = { + ASCII_I, ASCII_G, ASCII_N, ASCII_O, ASCII_R, ASCII_E, '\0' }; +static const char KW_IMPLIED[] = { + ASCII_I, ASCII_M, ASCII_P, ASCII_L, ASCII_I, ASCII_E, ASCII_D, '\0' }; +static const char KW_INCLUDE[] = { + ASCII_I, ASCII_N, ASCII_C, ASCII_L, ASCII_U, ASCII_D, ASCII_E, '\0' }; +static const char KW_NDATA[] = { + ASCII_N, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; +static const char KW_NMTOKEN[] = { + ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, '\0' }; +static const char KW_NMTOKENS[] = { + ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, ASCII_S, + '\0' }; +static const char KW_NOTATION[] = + { ASCII_N, ASCII_O, ASCII_T, ASCII_A, ASCII_T, ASCII_I, ASCII_O, ASCII_N, + '\0' }; +static const char KW_PCDATA[] = { + ASCII_P, ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; +static const char KW_PUBLIC[] = { + ASCII_P, ASCII_U, ASCII_B, ASCII_L, ASCII_I, ASCII_C, '\0' }; +static const char KW_REQUIRED[] = { + ASCII_R, ASCII_E, ASCII_Q, ASCII_U, ASCII_I, ASCII_R, ASCII_E, ASCII_D, + '\0' }; +static const char KW_SYSTEM[] = { + ASCII_S, ASCII_Y, ASCII_S, ASCII_T, ASCII_E, ASCII_M, '\0' }; + +#ifndef MIN_BYTES_PER_CHAR +#define MIN_BYTES_PER_CHAR(enc) ((enc)->minBytesPerChar) +#endif + +#ifdef XML_DTD +#define setTopLevel(state) \ + ((state)->handler = ((state)->documentEntity \ + ? internalSubset \ + : externalSubset1)) +#else /* not XML_DTD */ +#define setTopLevel(state) ((state)->handler = internalSubset) +#endif /* not XML_DTD */ + +typedef int PTRCALL PROLOG_HANDLER(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc); + +static PROLOG_HANDLER + prolog0, prolog1, prolog2, + doctype0, doctype1, doctype2, doctype3, doctype4, doctype5, + internalSubset, + entity0, entity1, entity2, entity3, entity4, entity5, entity6, + entity7, entity8, entity9, entity10, + notation0, notation1, notation2, notation3, notation4, + attlist0, attlist1, attlist2, attlist3, attlist4, attlist5, attlist6, + attlist7, attlist8, attlist9, + element0, element1, element2, element3, element4, element5, element6, + element7, +#ifdef XML_DTD + externalSubset0, externalSubset1, + condSect0, condSect1, condSect2, +#endif /* XML_DTD */ + declClose, + error; + +static int FASTCALL common(PROLOG_STATE *state, int tok); + +static int PTRCALL +prolog0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + state->handler = prolog1; + return XML_ROLE_NONE; + case XML_TOK_XML_DECL: + state->handler = prolog1; + return XML_ROLE_XML_DECL; + case XML_TOK_PI: + state->handler = prolog1; + return XML_ROLE_PI; + case XML_TOK_COMMENT: + state->handler = prolog1; + return XML_ROLE_COMMENT; + case XML_TOK_BOM: + return XML_ROLE_NONE; + case XML_TOK_DECL_OPEN: + if (!XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_DOCTYPE)) + break; + state->handler = doctype0; + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_INSTANCE_START: + state->handler = error; + return XML_ROLE_INSTANCE_START; + } + return common(state, tok); +} + +static int PTRCALL +prolog1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_PI: + return XML_ROLE_PI; + case XML_TOK_COMMENT: + return XML_ROLE_COMMENT; + case XML_TOK_BOM: + return XML_ROLE_NONE; + case XML_TOK_DECL_OPEN: + if (!XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_DOCTYPE)) + break; + state->handler = doctype0; + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_INSTANCE_START: + state->handler = error; + return XML_ROLE_INSTANCE_START; + } + return common(state, tok); +} + +static int PTRCALL +prolog2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_PI: + return XML_ROLE_PI; + case XML_TOK_COMMENT: + return XML_ROLE_COMMENT; + case XML_TOK_INSTANCE_START: + state->handler = error; + return XML_ROLE_INSTANCE_START; + } + return common(state, tok); +} + +static int PTRCALL +doctype0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = doctype1; + return XML_ROLE_DOCTYPE_NAME; + } + return common(state, tok); +} + +static int PTRCALL +doctype1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = internalSubset; + return XML_ROLE_DOCTYPE_INTERNAL_SUBSET; + case XML_TOK_DECL_CLOSE: + state->handler = prolog2; + return XML_ROLE_DOCTYPE_CLOSE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = doctype3; + return XML_ROLE_DOCTYPE_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = doctype2; + return XML_ROLE_DOCTYPE_NONE; + } + break; + } + return common(state, tok); +} + +static int PTRCALL +doctype2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_LITERAL: + state->handler = doctype3; + return XML_ROLE_DOCTYPE_PUBLIC_ID; + } + return common(state, tok); +} + +static int PTRCALL +doctype3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_LITERAL: + state->handler = doctype4; + return XML_ROLE_DOCTYPE_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +doctype4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = internalSubset; + return XML_ROLE_DOCTYPE_INTERNAL_SUBSET; + case XML_TOK_DECL_CLOSE: + state->handler = prolog2; + return XML_ROLE_DOCTYPE_CLOSE; + } + return common(state, tok); +} + +static int PTRCALL +doctype5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_DECL_CLOSE: + state->handler = prolog2; + return XML_ROLE_DOCTYPE_CLOSE; + } + return common(state, tok); +} + +static int PTRCALL +internalSubset(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_DECL_OPEN: + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_ENTITY)) { + state->handler = entity0; + return XML_ROLE_ENTITY_NONE; + } + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_ATTLIST)) { + state->handler = attlist0; + return XML_ROLE_ATTLIST_NONE; + } + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_ELEMENT)) { + state->handler = element0; + return XML_ROLE_ELEMENT_NONE; + } + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_NOTATION)) { + state->handler = notation0; + return XML_ROLE_NOTATION_NONE; + } + break; + case XML_TOK_PI: + return XML_ROLE_PI; + case XML_TOK_COMMENT: + return XML_ROLE_COMMENT; + case XML_TOK_PARAM_ENTITY_REF: + return XML_ROLE_PARAM_ENTITY_REF; + case XML_TOK_CLOSE_BRACKET: + state->handler = doctype5; + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_NONE: + return XML_ROLE_NONE; + } + return common(state, tok); +} + +#ifdef XML_DTD + +static int PTRCALL +externalSubset0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + state->handler = externalSubset1; + if (tok == XML_TOK_XML_DECL) + return XML_ROLE_TEXT_DECL; + return externalSubset1(state, tok, ptr, end, enc); +} + +static int PTRCALL +externalSubset1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_COND_SECT_OPEN: + state->handler = condSect0; + return XML_ROLE_NONE; + case XML_TOK_COND_SECT_CLOSE: + if (state->includeLevel == 0) + break; + state->includeLevel -= 1; + return XML_ROLE_NONE; + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_CLOSE_BRACKET: + break; + case XML_TOK_NONE: + if (state->includeLevel) + break; + return XML_ROLE_NONE; + default: + return internalSubset(state, tok, ptr, end, enc); + } + return common(state, tok); +} + +#endif /* XML_DTD */ + +static int PTRCALL +entity0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_PERCENT: + state->handler = entity1; + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + state->handler = entity2; + return XML_ROLE_GENERAL_ENTITY_NAME; + } + return common(state, tok); +} + +static int PTRCALL +entity1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + state->handler = entity7; + return XML_ROLE_PARAM_ENTITY_NAME; + } + return common(state, tok); +} + +static int PTRCALL +entity2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = entity4; + return XML_ROLE_ENTITY_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = entity3; + return XML_ROLE_ENTITY_NONE; + } + break; + case XML_TOK_LITERAL: + state->handler = declClose; + state->role_none = XML_ROLE_ENTITY_NONE; + return XML_ROLE_ENTITY_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +entity3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_LITERAL: + state->handler = entity4; + return XML_ROLE_ENTITY_PUBLIC_ID; + } + return common(state, tok); +} + +static int PTRCALL +entity4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_LITERAL: + state->handler = entity5; + return XML_ROLE_ENTITY_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +entity5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_ENTITY_COMPLETE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_NDATA)) { + state->handler = entity6; + return XML_ROLE_ENTITY_NONE; + } + break; + } + return common(state, tok); +} + +static int PTRCALL +entity6(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + state->handler = declClose; + state->role_none = XML_ROLE_ENTITY_NONE; + return XML_ROLE_ENTITY_NOTATION_NAME; + } + return common(state, tok); +} + +static int PTRCALL +entity7(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = entity9; + return XML_ROLE_ENTITY_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = entity8; + return XML_ROLE_ENTITY_NONE; + } + break; + case XML_TOK_LITERAL: + state->handler = declClose; + state->role_none = XML_ROLE_ENTITY_NONE; + return XML_ROLE_ENTITY_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +entity8(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_LITERAL: + state->handler = entity9; + return XML_ROLE_ENTITY_PUBLIC_ID; + } + return common(state, tok); +} + +static int PTRCALL +entity9(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_LITERAL: + state->handler = entity10; + return XML_ROLE_ENTITY_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +entity10(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_ENTITY_COMPLETE; + } + return common(state, tok); +} + +static int PTRCALL +notation0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_NAME: + state->handler = notation1; + return XML_ROLE_NOTATION_NAME; + } + return common(state, tok); +} + +static int PTRCALL +notation1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = notation3; + return XML_ROLE_NOTATION_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = notation2; + return XML_ROLE_NOTATION_NONE; + } + break; + } + return common(state, tok); +} + +static int PTRCALL +notation2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_LITERAL: + state->handler = notation4; + return XML_ROLE_NOTATION_PUBLIC_ID; + } + return common(state, tok); +} + +static int PTRCALL +notation3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_LITERAL: + state->handler = declClose; + state->role_none = XML_ROLE_NOTATION_NONE; + return XML_ROLE_NOTATION_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +notation4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_LITERAL: + state->handler = declClose; + state->role_none = XML_ROLE_NOTATION_NONE; + return XML_ROLE_NOTATION_SYSTEM_ID; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_NOTATION_NO_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +attlist0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = attlist1; + return XML_ROLE_ATTLIST_ELEMENT_NAME; + } + return common(state, tok); +} + +static int PTRCALL +attlist1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = attlist2; + return XML_ROLE_ATTRIBUTE_NAME; + } + return common(state, tok); +} + +static int PTRCALL +attlist2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NAME: + { + static const char * const types[] = { + KW_CDATA, + KW_ID, + KW_IDREF, + KW_IDREFS, + KW_ENTITY, + KW_ENTITIES, + KW_NMTOKEN, + KW_NMTOKENS, + }; + int i; + for (i = 0; i < (int)(sizeof(types)/sizeof(types[0])); i++) + if (XmlNameMatchesAscii(enc, ptr, end, types[i])) { + state->handler = attlist8; + return XML_ROLE_ATTRIBUTE_TYPE_CDATA + i; + } + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_NOTATION)) { + state->handler = attlist5; + return XML_ROLE_ATTLIST_NONE; + } + break; + case XML_TOK_OPEN_PAREN: + state->handler = attlist3; + return XML_ROLE_ATTLIST_NONE; + } + return common(state, tok); +} + +static int PTRCALL +attlist3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NMTOKEN: + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = attlist4; + return XML_ROLE_ATTRIBUTE_ENUM_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +attlist4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_CLOSE_PAREN: + state->handler = attlist8; + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_OR: + state->handler = attlist3; + return XML_ROLE_ATTLIST_NONE; + } + return common(state, tok); +} + +static int PTRCALL +attlist5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_OPEN_PAREN: + state->handler = attlist6; + return XML_ROLE_ATTLIST_NONE; + } + return common(state, tok); +} + +static int PTRCALL +attlist6(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NAME: + state->handler = attlist7; + return XML_ROLE_ATTRIBUTE_NOTATION_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +attlist7(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_CLOSE_PAREN: + state->handler = attlist8; + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_OR: + state->handler = attlist6; + return XML_ROLE_ATTLIST_NONE; + } + return common(state, tok); +} + +/* default value */ +static int PTRCALL +attlist8(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_POUND_NAME: + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_IMPLIED)) { + state->handler = attlist1; + return XML_ROLE_IMPLIED_ATTRIBUTE_VALUE; + } + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_REQUIRED)) { + state->handler = attlist1; + return XML_ROLE_REQUIRED_ATTRIBUTE_VALUE; + } + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_FIXED)) { + state->handler = attlist9; + return XML_ROLE_ATTLIST_NONE; + } + break; + case XML_TOK_LITERAL: + state->handler = attlist1; + return XML_ROLE_DEFAULT_ATTRIBUTE_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +attlist9(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_LITERAL: + state->handler = attlist1; + return XML_ROLE_FIXED_ATTRIBUTE_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +element0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element1; + return XML_ROLE_ELEMENT_NAME; + } + return common(state, tok); +} + +static int PTRCALL +element1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_EMPTY)) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_CONTENT_EMPTY; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_ANY)) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_CONTENT_ANY; + } + break; + case XML_TOK_OPEN_PAREN: + state->handler = element2; + state->level = 1; + return XML_ROLE_GROUP_OPEN; + } + return common(state, tok); +} + +static int PTRCALL +element2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_POUND_NAME: + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_PCDATA)) { + state->handler = element3; + return XML_ROLE_CONTENT_PCDATA; + } + break; + case XML_TOK_OPEN_PAREN: + state->level = 2; + state->handler = element6; + return XML_ROLE_GROUP_OPEN; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT; + case XML_TOK_NAME_QUESTION: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_OPT; + case XML_TOK_NAME_ASTERISK: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_REP; + case XML_TOK_NAME_PLUS: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_PLUS; + } + return common(state, tok); +} + +static int PTRCALL +element3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_CLOSE_PAREN: + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_GROUP_CLOSE; + case XML_TOK_CLOSE_PAREN_ASTERISK: + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_GROUP_CLOSE_REP; + case XML_TOK_OR: + state->handler = element4; + return XML_ROLE_ELEMENT_NONE; + } + return common(state, tok); +} + +static int PTRCALL +element4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element5; + return XML_ROLE_CONTENT_ELEMENT; + } + return common(state, tok); +} + +static int PTRCALL +element5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_CLOSE_PAREN_ASTERISK: + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_GROUP_CLOSE_REP; + case XML_TOK_OR: + state->handler = element4; + return XML_ROLE_ELEMENT_NONE; + } + return common(state, tok); +} + +static int PTRCALL +element6(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_OPEN_PAREN: + state->level += 1; + return XML_ROLE_GROUP_OPEN; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT; + case XML_TOK_NAME_QUESTION: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_OPT; + case XML_TOK_NAME_ASTERISK: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_REP; + case XML_TOK_NAME_PLUS: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_PLUS; + } + return common(state, tok); +} + +static int PTRCALL +element7(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_CLOSE_PAREN: + state->level -= 1; + if (state->level == 0) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + } + return XML_ROLE_GROUP_CLOSE; + case XML_TOK_CLOSE_PAREN_ASTERISK: + state->level -= 1; + if (state->level == 0) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + } + return XML_ROLE_GROUP_CLOSE_REP; + case XML_TOK_CLOSE_PAREN_QUESTION: + state->level -= 1; + if (state->level == 0) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + } + return XML_ROLE_GROUP_CLOSE_OPT; + case XML_TOK_CLOSE_PAREN_PLUS: + state->level -= 1; + if (state->level == 0) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + } + return XML_ROLE_GROUP_CLOSE_PLUS; + case XML_TOK_COMMA: + state->handler = element6; + return XML_ROLE_GROUP_SEQUENCE; + case XML_TOK_OR: + state->handler = element6; + return XML_ROLE_GROUP_CHOICE; + } + return common(state, tok); +} + +#ifdef XML_DTD + +static int PTRCALL +condSect0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_INCLUDE)) { + state->handler = condSect1; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_IGNORE)) { + state->handler = condSect2; + return XML_ROLE_NONE; + } + break; + } + return common(state, tok); +} + +static int PTRCALL +condSect1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = externalSubset1; + state->includeLevel += 1; + return XML_ROLE_NONE; + } + return common(state, tok); +} + +static int PTRCALL +condSect2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = externalSubset1; + return XML_ROLE_IGNORE_SECT; + } + return common(state, tok); +} + +#endif /* XML_DTD */ + +static int PTRCALL +declClose(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return state->role_none; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return state->role_none; + } + return common(state, tok); +} + +static int PTRCALL +error(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + return XML_ROLE_NONE; +} + +static int FASTCALL +common(PROLOG_STATE *state, int tok) +{ +#ifdef XML_DTD + if (!state->documentEntity && tok == XML_TOK_PARAM_ENTITY_REF) + return XML_ROLE_INNER_PARAM_ENTITY_REF; +#endif + state->handler = error; + return XML_ROLE_ERROR; +} + +void +XmlPrologStateInit(PROLOG_STATE *state) +{ + state->handler = prolog0; +#ifdef XML_DTD + state->documentEntity = 1; + state->includeLevel = 0; + state->inEntityValue = 0; +#endif /* XML_DTD */ +} + +#ifdef XML_DTD + +void +XmlPrologStateInitExternalEntity(PROLOG_STATE *state) +{ + state->handler = externalSubset0; + state->documentEntity = 0; + state->includeLevel = 0; +} + +#endif /* XML_DTD */ diff --git a/src/lib/expat/xmlrole.h b/src/lib/expat/xmlrole.h new file mode 100644 index 00000000000..4dd9f06f976 --- /dev/null +++ b/src/lib/expat/xmlrole.h @@ -0,0 +1,114 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#ifndef XmlRole_INCLUDED +#define XmlRole_INCLUDED 1 + +#ifdef __VMS +/* 0 1 2 3 0 1 2 3 + 1234567890123456789012345678901 1234567890123456789012345678901 */ +#define XmlPrologStateInitExternalEntity XmlPrologStateInitExternalEnt +#endif + +#include "xmltok.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + XML_ROLE_ERROR = -1, + XML_ROLE_NONE = 0, + XML_ROLE_XML_DECL, + XML_ROLE_INSTANCE_START, + XML_ROLE_DOCTYPE_NONE, + XML_ROLE_DOCTYPE_NAME, + XML_ROLE_DOCTYPE_SYSTEM_ID, + XML_ROLE_DOCTYPE_PUBLIC_ID, + XML_ROLE_DOCTYPE_INTERNAL_SUBSET, + XML_ROLE_DOCTYPE_CLOSE, + XML_ROLE_GENERAL_ENTITY_NAME, + XML_ROLE_PARAM_ENTITY_NAME, + XML_ROLE_ENTITY_NONE, + XML_ROLE_ENTITY_VALUE, + XML_ROLE_ENTITY_SYSTEM_ID, + XML_ROLE_ENTITY_PUBLIC_ID, + XML_ROLE_ENTITY_COMPLETE, + XML_ROLE_ENTITY_NOTATION_NAME, + XML_ROLE_NOTATION_NONE, + XML_ROLE_NOTATION_NAME, + XML_ROLE_NOTATION_SYSTEM_ID, + XML_ROLE_NOTATION_NO_SYSTEM_ID, + XML_ROLE_NOTATION_PUBLIC_ID, + XML_ROLE_ATTRIBUTE_NAME, + XML_ROLE_ATTRIBUTE_TYPE_CDATA, + XML_ROLE_ATTRIBUTE_TYPE_ID, + XML_ROLE_ATTRIBUTE_TYPE_IDREF, + XML_ROLE_ATTRIBUTE_TYPE_IDREFS, + XML_ROLE_ATTRIBUTE_TYPE_ENTITY, + XML_ROLE_ATTRIBUTE_TYPE_ENTITIES, + XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN, + XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS, + XML_ROLE_ATTRIBUTE_ENUM_VALUE, + XML_ROLE_ATTRIBUTE_NOTATION_VALUE, + XML_ROLE_ATTLIST_NONE, + XML_ROLE_ATTLIST_ELEMENT_NAME, + XML_ROLE_IMPLIED_ATTRIBUTE_VALUE, + XML_ROLE_REQUIRED_ATTRIBUTE_VALUE, + XML_ROLE_DEFAULT_ATTRIBUTE_VALUE, + XML_ROLE_FIXED_ATTRIBUTE_VALUE, + XML_ROLE_ELEMENT_NONE, + XML_ROLE_ELEMENT_NAME, + XML_ROLE_CONTENT_ANY, + XML_ROLE_CONTENT_EMPTY, + XML_ROLE_CONTENT_PCDATA, + XML_ROLE_GROUP_OPEN, + XML_ROLE_GROUP_CLOSE, + XML_ROLE_GROUP_CLOSE_REP, + XML_ROLE_GROUP_CLOSE_OPT, + XML_ROLE_GROUP_CLOSE_PLUS, + XML_ROLE_GROUP_CHOICE, + XML_ROLE_GROUP_SEQUENCE, + XML_ROLE_CONTENT_ELEMENT, + XML_ROLE_CONTENT_ELEMENT_REP, + XML_ROLE_CONTENT_ELEMENT_OPT, + XML_ROLE_CONTENT_ELEMENT_PLUS, + XML_ROLE_PI, + XML_ROLE_COMMENT, +#ifdef XML_DTD + XML_ROLE_TEXT_DECL, + XML_ROLE_IGNORE_SECT, + XML_ROLE_INNER_PARAM_ENTITY_REF, +#endif /* XML_DTD */ + XML_ROLE_PARAM_ENTITY_REF +}; + +typedef struct prolog_state { + int (PTRCALL *handler) (struct prolog_state *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc); + unsigned level; + int role_none; +#ifdef XML_DTD + unsigned includeLevel; + int documentEntity; + int inEntityValue; +#endif /* XML_DTD */ +} PROLOG_STATE; + +void XmlPrologStateInit(PROLOG_STATE *); +#ifdef XML_DTD +void XmlPrologStateInitExternalEntity(PROLOG_STATE *); +#endif /* XML_DTD */ + +#define XmlTokenRole(state, tok, ptr, end, enc) \ + (((state)->handler)(state, tok, ptr, end, enc)) + +#ifdef __cplusplus +} +#endif + +#endif /* not XmlRole_INCLUDED */ diff --git a/src/lib/expat/xmltok.c b/src/lib/expat/xmltok.c new file mode 100644 index 00000000000..3a7275adb25 --- /dev/null +++ b/src/lib/expat/xmltok.c @@ -0,0 +1,1629 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#include <stddef.h> + +#include "intconfig.h" + +#include "expat_external.h" +#include "internal.h" +#include "xmltok.h" +#include "nametab.h" + +#ifdef XML_DTD +#define IGNORE_SECTION_TOK_VTABLE , PREFIX(ignoreSectionTok) +#else +#define IGNORE_SECTION_TOK_VTABLE /* as nothing */ +#endif + +#define VTABLE1 \ + { PREFIX(prologTok), PREFIX(contentTok), \ + PREFIX(cdataSectionTok) IGNORE_SECTION_TOK_VTABLE }, \ + { PREFIX(attributeValueTok), PREFIX(entityValueTok) }, \ + PREFIX(sameName), \ + PREFIX(nameMatchesAscii), \ + PREFIX(nameLength), \ + PREFIX(skipS), \ + PREFIX(getAtts), \ + PREFIX(charRefNumber), \ + PREFIX(predefinedEntityName), \ + PREFIX(updatePosition), \ + PREFIX(isPublicId) + +#define VTABLE VTABLE1, PREFIX(toUtf8), PREFIX(toUtf16) + +#define UCS2_GET_NAMING(pages, hi, lo) \ + (namingBitmap[(pages[hi] << 3) + ((lo) >> 5)] & (1 << ((lo) & 0x1F))) + +/* A 2 byte UTF-8 representation splits the characters 11 bits between + the bottom 5 and 6 bits of the bytes. We need 8 bits to index into + pages, 3 bits to add to that index and 5 bits to generate the mask. +*/ +#define UTF8_GET_NAMING2(pages, byte) \ + (namingBitmap[((pages)[(((byte)[0]) >> 2) & 7] << 3) \ + + ((((byte)[0]) & 3) << 1) \ + + ((((byte)[1]) >> 5) & 1)] \ + & (1 << (((byte)[1]) & 0x1F))) + +/* A 3 byte UTF-8 representation splits the characters 16 bits between + the bottom 4, 6 and 6 bits of the bytes. We need 8 bits to index + into pages, 3 bits to add to that index and 5 bits to generate the + mask. +*/ +#define UTF8_GET_NAMING3(pages, byte) \ + (namingBitmap[((pages)[((((byte)[0]) & 0xF) << 4) \ + + ((((byte)[1]) >> 2) & 0xF)] \ + << 3) \ + + ((((byte)[1]) & 3) << 1) \ + + ((((byte)[2]) >> 5) & 1)] \ + & (1 << (((byte)[2]) & 0x1F))) + +#define UTF8_GET_NAMING(pages, p, n) \ + ((n) == 2 \ + ? UTF8_GET_NAMING2(pages, (const unsigned char *)(p)) \ + : ((n) == 3 \ + ? UTF8_GET_NAMING3(pages, (const unsigned char *)(p)) \ + : 0)) + +/* Detection of invalid UTF-8 sequences is based on Table 3.1B + of Unicode 3.2: http://www.unicode.org/unicode/reports/tr28/ + with the additional restriction of not allowing the Unicode + code points 0xFFFF and 0xFFFE (sequences EF,BF,BF and EF,BF,BE). + Implementation details: + (A & 0x80) == 0 means A < 0x80 + and + (A & 0xC0) == 0xC0 means A > 0xBF +*/ + +#define UTF8_INVALID2(p) \ + ((*p) < 0xC2 || ((p)[1] & 0x80) == 0 || ((p)[1] & 0xC0) == 0xC0) + +#define UTF8_INVALID3(p) \ + (((p)[2] & 0x80) == 0 \ + || \ + ((*p) == 0xEF && (p)[1] == 0xBF \ + ? \ + (p)[2] > 0xBD \ + : \ + ((p)[2] & 0xC0) == 0xC0) \ + || \ + ((*p) == 0xE0 \ + ? \ + (p)[1] < 0xA0 || ((p)[1] & 0xC0) == 0xC0 \ + : \ + ((p)[1] & 0x80) == 0 \ + || \ + ((*p) == 0xED ? (p)[1] > 0x9F : ((p)[1] & 0xC0) == 0xC0))) + +#define UTF8_INVALID4(p) \ + (((p)[3] & 0x80) == 0 || ((p)[3] & 0xC0) == 0xC0 \ + || \ + ((p)[2] & 0x80) == 0 || ((p)[2] & 0xC0) == 0xC0 \ + || \ + ((*p) == 0xF0 \ + ? \ + (p)[1] < 0x90 || ((p)[1] & 0xC0) == 0xC0 \ + : \ + ((p)[1] & 0x80) == 0 \ + || \ + ((*p) == 0xF4 ? (p)[1] > 0x8F : ((p)[1] & 0xC0) == 0xC0))) + +static int PTRFASTCALL +isNever(const ENCODING *enc, const char *p) +{ + return 0; +} + +static int PTRFASTCALL +utf8_isName2(const ENCODING *enc, const char *p) +{ + return UTF8_GET_NAMING2(namePages, (const unsigned char *)p); +} + +static int PTRFASTCALL +utf8_isName3(const ENCODING *enc, const char *p) +{ + return UTF8_GET_NAMING3(namePages, (const unsigned char *)p); +} + +#define utf8_isName4 isNever + +static int PTRFASTCALL +utf8_isNmstrt2(const ENCODING *enc, const char *p) +{ + return UTF8_GET_NAMING2(nmstrtPages, (const unsigned char *)p); +} + +static int PTRFASTCALL +utf8_isNmstrt3(const ENCODING *enc, const char *p) +{ + return UTF8_GET_NAMING3(nmstrtPages, (const unsigned char *)p); +} + +#define utf8_isNmstrt4 isNever + +static int PTRFASTCALL +utf8_isInvalid2(const ENCODING *enc, const char *p) +{ + return UTF8_INVALID2((const unsigned char *)p); +} + +static int PTRFASTCALL +utf8_isInvalid3(const ENCODING *enc, const char *p) +{ + return UTF8_INVALID3((const unsigned char *)p); +} + +static int PTRFASTCALL +utf8_isInvalid4(const ENCODING *enc, const char *p) +{ + return UTF8_INVALID4((const unsigned char *)p); +} + +struct normal_encoding { + ENCODING enc; + unsigned char type[256]; +#ifdef XML_MIN_SIZE + int (PTRFASTCALL *byteType)(const ENCODING *, const char *); + int (PTRFASTCALL *isNameMin)(const ENCODING *, const char *); + int (PTRFASTCALL *isNmstrtMin)(const ENCODING *, const char *); + int (PTRFASTCALL *byteToAscii)(const ENCODING *, const char *); + int (PTRCALL *charMatches)(const ENCODING *, const char *, int); +#endif /* XML_MIN_SIZE */ + int (PTRFASTCALL *isName2)(const ENCODING *, const char *); + int (PTRFASTCALL *isName3)(const ENCODING *, const char *); + int (PTRFASTCALL *isName4)(const ENCODING *, const char *); + int (PTRFASTCALL *isNmstrt2)(const ENCODING *, const char *); + int (PTRFASTCALL *isNmstrt3)(const ENCODING *, const char *); + int (PTRFASTCALL *isNmstrt4)(const ENCODING *, const char *); + int (PTRFASTCALL *isInvalid2)(const ENCODING *, const char *); + int (PTRFASTCALL *isInvalid3)(const ENCODING *, const char *); + int (PTRFASTCALL *isInvalid4)(const ENCODING *, const char *); +}; + +#define AS_NORMAL_ENCODING(enc) ((const struct normal_encoding *) (enc)) + +#ifdef XML_MIN_SIZE + +#define STANDARD_VTABLE(E) \ + E ## byteType, \ + E ## isNameMin, \ + E ## isNmstrtMin, \ + E ## byteToAscii, \ + E ## charMatches, + +#else + +#define STANDARD_VTABLE(E) /* as nothing */ + +#endif + +#define NORMAL_VTABLE(E) \ + E ## isName2, \ + E ## isName3, \ + E ## isName4, \ + E ## isNmstrt2, \ + E ## isNmstrt3, \ + E ## isNmstrt4, \ + E ## isInvalid2, \ + E ## isInvalid3, \ + E ## isInvalid4 + +static int FASTCALL checkCharRefNumber(int); + +#include "xmltok_impl.h" +#include "ascii.h" + +#ifdef XML_MIN_SIZE +#define sb_isNameMin isNever +#define sb_isNmstrtMin isNever +#endif + +#ifdef XML_MIN_SIZE +#define MINBPC(enc) ((enc)->minBytesPerChar) +#else +/* minimum bytes per character */ +#define MINBPC(enc) 1 +#endif + +#define SB_BYTE_TYPE(enc, p) \ + (((struct normal_encoding *)(enc))->type[(unsigned char)*(p)]) + +#ifdef XML_MIN_SIZE +static int PTRFASTCALL +sb_byteType(const ENCODING *enc, const char *p) +{ + return SB_BYTE_TYPE(enc, p); +} +#define BYTE_TYPE(enc, p) \ + (AS_NORMAL_ENCODING(enc)->byteType(enc, p)) +#else +#define BYTE_TYPE(enc, p) SB_BYTE_TYPE(enc, p) +#endif + +#ifdef XML_MIN_SIZE +#define BYTE_TO_ASCII(enc, p) \ + (AS_NORMAL_ENCODING(enc)->byteToAscii(enc, p)) +static int PTRFASTCALL +sb_byteToAscii(const ENCODING *enc, const char *p) +{ + return *p; +} +#else +#define BYTE_TO_ASCII(enc, p) (*(p)) +#endif + +#define IS_NAME_CHAR(enc, p, n) \ + (AS_NORMAL_ENCODING(enc)->isName ## n(enc, p)) +#define IS_NMSTRT_CHAR(enc, p, n) \ + (AS_NORMAL_ENCODING(enc)->isNmstrt ## n(enc, p)) +#define IS_INVALID_CHAR(enc, p, n) \ + (AS_NORMAL_ENCODING(enc)->isInvalid ## n(enc, p)) + +#ifdef XML_MIN_SIZE +#define IS_NAME_CHAR_MINBPC(enc, p) \ + (AS_NORMAL_ENCODING(enc)->isNameMin(enc, p)) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) \ + (AS_NORMAL_ENCODING(enc)->isNmstrtMin(enc, p)) +#else +#define IS_NAME_CHAR_MINBPC(enc, p) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) (0) +#endif + +#ifdef XML_MIN_SIZE +#define CHAR_MATCHES(enc, p, c) \ + (AS_NORMAL_ENCODING(enc)->charMatches(enc, p, c)) +static int PTRCALL +sb_charMatches(const ENCODING *enc, const char *p, int c) +{ + return *p == c; +} +#else +/* c is an ASCII character */ +#define CHAR_MATCHES(enc, p, c) (*(p) == c) +#endif + +#define PREFIX(ident) normal_ ## ident +#include "xmltok_impl.c" + +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR + +enum { /* UTF8_cvalN is value of masked first byte of N byte sequence */ + UTF8_cval1 = 0x00, + UTF8_cval2 = 0xc0, + UTF8_cval3 = 0xe0, + UTF8_cval4 = 0xf0 +}; + +static void PTRCALL +utf8_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + char *to; + const char *from; + if (fromLim - *fromP > toLim - *toP) { + /* Avoid copying partial characters. */ + for (fromLim = *fromP + (toLim - *toP); fromLim > *fromP; fromLim--) + if (((unsigned char)fromLim[-1] & 0xc0) != 0x80) + break; + } + for (to = *toP, from = *fromP; from != fromLim; from++, to++) + *to = *from; + *fromP = from; + *toP = to; +} + +static void PTRCALL +utf8_toUtf16(const ENCODING *enc, + const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) +{ + unsigned short *to = *toP; + const char *from = *fromP; + while (from != fromLim && to != toLim) { + switch (((struct normal_encoding *)enc)->type[(unsigned char)*from]) { + case BT_LEAD2: + *to++ = (unsigned short)(((from[0] & 0x1f) << 6) | (from[1] & 0x3f)); + from += 2; + break; + case BT_LEAD3: + *to++ = (unsigned short)(((from[0] & 0xf) << 12) + | ((from[1] & 0x3f) << 6) | (from[2] & 0x3f)); + from += 3; + break; + case BT_LEAD4: + { + unsigned long n; + if (to + 1 == toLim) + goto after; + n = ((from[0] & 0x7) << 18) | ((from[1] & 0x3f) << 12) + | ((from[2] & 0x3f) << 6) | (from[3] & 0x3f); + n -= 0x10000; + to[0] = (unsigned short)((n >> 10) | 0xD800); + to[1] = (unsigned short)((n & 0x3FF) | 0xDC00); + to += 2; + from += 4; + } + break; + default: + *to++ = *from++; + break; + } + } +after: + *fromP = from; + *toP = to; +} + +#ifdef XML_NS +static const struct normal_encoding utf8_encoding_ns = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#include "asciitab.h" +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; +#endif + +static const struct normal_encoding utf8_encoding = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; + +#ifdef XML_NS + +static const struct normal_encoding internal_utf8_encoding_ns = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#include "iasciitab.h" +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; + +#endif + +static const struct normal_encoding internal_utf8_encoding = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "iasciitab.h" +#undef BT_COLON +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; + +static void PTRCALL +latin1_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + for (;;) { + unsigned char c; + if (*fromP == fromLim) + break; + c = (unsigned char)**fromP; + if (c & 0x80) { + if (toLim - *toP < 2) + break; + *(*toP)++ = (char)((c >> 6) | UTF8_cval2); + *(*toP)++ = (char)((c & 0x3f) | 0x80); + (*fromP)++; + } + else { + if (*toP == toLim) + break; + *(*toP)++ = *(*fromP)++; + } + } +} + +static void PTRCALL +latin1_toUtf16(const ENCODING *enc, + const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) +{ + while (*fromP != fromLim && *toP != toLim) + *(*toP)++ = (unsigned char)*(*fromP)++; +} + +#ifdef XML_NS + +static const struct normal_encoding latin1_encoding_ns = { + { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 }, + { +#include "asciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(sb_) +}; + +#endif + +static const struct normal_encoding latin1_encoding = { + { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(sb_) +}; + +static void PTRCALL +ascii_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + while (*fromP != fromLim && *toP != toLim) + *(*toP)++ = *(*fromP)++; +} + +#ifdef XML_NS + +static const struct normal_encoding ascii_encoding_ns = { + { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 }, + { +#include "asciitab.h" +/* BT_NONXML == 0 */ + }, + STANDARD_VTABLE(sb_) +}; + +#endif + +static const struct normal_encoding ascii_encoding = { + { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +/* BT_NONXML == 0 */ + }, + STANDARD_VTABLE(sb_) +}; + +static int PTRFASTCALL +unicode_byte_type(char hi, char lo) +{ + switch ((unsigned char)hi) { + case 0xD8: case 0xD9: case 0xDA: case 0xDB: + return BT_LEAD4; + case 0xDC: case 0xDD: case 0xDE: case 0xDF: + return BT_TRAIL; + case 0xFF: + switch ((unsigned char)lo) { + case 0xFF: + case 0xFE: + return BT_NONXML; + } + break; + } + return BT_NONASCII; +} + +#define DEFINE_UTF16_TO_UTF8(E) \ +static void PTRCALL \ +E ## toUtf8(const ENCODING *enc, \ + const char **fromP, const char *fromLim, \ + char **toP, const char *toLim) \ +{ \ + const char *from; \ + for (from = *fromP; from != fromLim; from += 2) { \ + int plane; \ + unsigned char lo2; \ + unsigned char lo = GET_LO(from); \ + unsigned char hi = GET_HI(from); \ + switch (hi) { \ + case 0: \ + if (lo < 0x80) { \ + if (*toP == toLim) { \ + *fromP = from; \ + return; \ + } \ + *(*toP)++ = lo; \ + break; \ + } \ + /* fall through */ \ + case 0x1: case 0x2: case 0x3: \ + case 0x4: case 0x5: case 0x6: case 0x7: \ + if (toLim - *toP < 2) { \ + *fromP = from; \ + return; \ + } \ + *(*toP)++ = ((lo >> 6) | (hi << 2) | UTF8_cval2); \ + *(*toP)++ = ((lo & 0x3f) | 0x80); \ + break; \ + default: \ + if (toLim - *toP < 3) { \ + *fromP = from; \ + return; \ + } \ + /* 16 bits divided 4, 6, 6 amongst 3 bytes */ \ + *(*toP)++ = ((hi >> 4) | UTF8_cval3); \ + *(*toP)++ = (((hi & 0xf) << 2) | (lo >> 6) | 0x80); \ + *(*toP)++ = ((lo & 0x3f) | 0x80); \ + break; \ + case 0xD8: case 0xD9: case 0xDA: case 0xDB: \ + if (toLim - *toP < 4) { \ + *fromP = from; \ + return; \ + } \ + plane = (((hi & 0x3) << 2) | ((lo >> 6) & 0x3)) + 1; \ + *(*toP)++ = ((plane >> 2) | UTF8_cval4); \ + *(*toP)++ = (((lo >> 2) & 0xF) | ((plane & 0x3) << 4) | 0x80); \ + from += 2; \ + lo2 = GET_LO(from); \ + *(*toP)++ = (((lo & 0x3) << 4) \ + | ((GET_HI(from) & 0x3) << 2) \ + | (lo2 >> 6) \ + | 0x80); \ + *(*toP)++ = ((lo2 & 0x3f) | 0x80); \ + break; \ + } \ + } \ + *fromP = from; \ +} + +#define DEFINE_UTF16_TO_UTF16(E) \ +static void PTRCALL \ +E ## toUtf16(const ENCODING *enc, \ + const char **fromP, const char *fromLim, \ + unsigned short **toP, const unsigned short *toLim) \ +{ \ + /* Avoid copying first half only of surrogate */ \ + if (fromLim - *fromP > ((toLim - *toP) << 1) \ + && (GET_HI(fromLim - 2) & 0xF8) == 0xD8) \ + fromLim -= 2; \ + for (; *fromP != fromLim && *toP != toLim; *fromP += 2) \ + *(*toP)++ = (GET_HI(*fromP) << 8) | GET_LO(*fromP); \ +} + +#define SET2(ptr, ch) \ + (((ptr)[0] = ((ch) & 0xff)), ((ptr)[1] = ((ch) >> 8))) +#define GET_LO(ptr) ((unsigned char)(ptr)[0]) +#define GET_HI(ptr) ((unsigned char)(ptr)[1]) + +DEFINE_UTF16_TO_UTF8(little2_) +DEFINE_UTF16_TO_UTF16(little2_) + +#undef SET2 +#undef GET_LO +#undef GET_HI + +#define SET2(ptr, ch) \ + (((ptr)[0] = ((ch) >> 8)), ((ptr)[1] = ((ch) & 0xFF))) +#define GET_LO(ptr) ((unsigned char)(ptr)[1]) +#define GET_HI(ptr) ((unsigned char)(ptr)[0]) + +DEFINE_UTF16_TO_UTF8(big2_) +DEFINE_UTF16_TO_UTF16(big2_) + +#undef SET2 +#undef GET_LO +#undef GET_HI + +#define LITTLE2_BYTE_TYPE(enc, p) \ + ((p)[1] == 0 \ + ? ((struct normal_encoding *)(enc))->type[(unsigned char)*(p)] \ + : unicode_byte_type((p)[1], (p)[0])) +#define LITTLE2_BYTE_TO_ASCII(enc, p) ((p)[1] == 0 ? (p)[0] : -1) +#define LITTLE2_CHAR_MATCHES(enc, p, c) ((p)[1] == 0 && (p)[0] == c) +#define LITTLE2_IS_NAME_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(namePages, (unsigned char)p[1], (unsigned char)p[0]) +#define LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[1], (unsigned char)p[0]) + +#ifdef XML_MIN_SIZE + +static int PTRFASTCALL +little2_byteType(const ENCODING *enc, const char *p) +{ + return LITTLE2_BYTE_TYPE(enc, p); +} + +static int PTRFASTCALL +little2_byteToAscii(const ENCODING *enc, const char *p) +{ + return LITTLE2_BYTE_TO_ASCII(enc, p); +} + +static int PTRCALL +little2_charMatches(const ENCODING *enc, const char *p, int c) +{ + return LITTLE2_CHAR_MATCHES(enc, p, c); +} + +static int PTRFASTCALL +little2_isNameMin(const ENCODING *enc, const char *p) +{ + return LITTLE2_IS_NAME_CHAR_MINBPC(enc, p); +} + +static int PTRFASTCALL +little2_isNmstrtMin(const ENCODING *enc, const char *p) +{ + return LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p); +} + +#undef VTABLE +#define VTABLE VTABLE1, little2_toUtf8, little2_toUtf16 + +#else /* not XML_MIN_SIZE */ + +#undef PREFIX +#define PREFIX(ident) little2_ ## ident +#define MINBPC(enc) 2 +/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */ +#define BYTE_TYPE(enc, p) LITTLE2_BYTE_TYPE(enc, p) +#define BYTE_TO_ASCII(enc, p) LITTLE2_BYTE_TO_ASCII(enc, p) +#define CHAR_MATCHES(enc, p, c) LITTLE2_CHAR_MATCHES(enc, p, c) +#define IS_NAME_CHAR(enc, p, n) 0 +#define IS_NAME_CHAR_MINBPC(enc, p) LITTLE2_IS_NAME_CHAR_MINBPC(enc, p) +#define IS_NMSTRT_CHAR(enc, p, n) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p) + +#include "xmltok_impl.c" + +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR + +#endif /* not XML_MIN_SIZE */ + +#ifdef XML_NS + +static const struct normal_encoding little2_encoding_ns = { + { VTABLE, 2, 0, +#if BYTEORDER == 1234 + 1 +#else + 0 +#endif + }, + { +#include "asciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) +}; + +#endif + +static const struct normal_encoding little2_encoding = { + { VTABLE, 2, 0, +#if BYTEORDER == 1234 + 1 +#else + 0 +#endif + }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) +}; + +#if BYTEORDER != 4321 + +#ifdef XML_NS + +static const struct normal_encoding internal_little2_encoding_ns = { + { VTABLE, 2, 0, 1 }, + { +#include "iasciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) +}; + +#endif + +static const struct normal_encoding internal_little2_encoding = { + { VTABLE, 2, 0, 1 }, + { +#define BT_COLON BT_NMSTRT +#include "iasciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) +}; + +#endif + + +#define BIG2_BYTE_TYPE(enc, p) \ + ((p)[0] == 0 \ + ? ((struct normal_encoding *)(enc))->type[(unsigned char)(p)[1]] \ + : unicode_byte_type((p)[0], (p)[1])) +#define BIG2_BYTE_TO_ASCII(enc, p) ((p)[0] == 0 ? (p)[1] : -1) +#define BIG2_CHAR_MATCHES(enc, p, c) ((p)[0] == 0 && (p)[1] == c) +#define BIG2_IS_NAME_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(namePages, (unsigned char)p[0], (unsigned char)p[1]) +#define BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[0], (unsigned char)p[1]) + +#ifdef XML_MIN_SIZE + +static int PTRFASTCALL +big2_byteType(const ENCODING *enc, const char *p) +{ + return BIG2_BYTE_TYPE(enc, p); +} + +static int PTRFASTCALL +big2_byteToAscii(const ENCODING *enc, const char *p) +{ + return BIG2_BYTE_TO_ASCII(enc, p); +} + +static int PTRCALL +big2_charMatches(const ENCODING *enc, const char *p, int c) +{ + return BIG2_CHAR_MATCHES(enc, p, c); +} + +static int PTRFASTCALL +big2_isNameMin(const ENCODING *enc, const char *p) +{ + return BIG2_IS_NAME_CHAR_MINBPC(enc, p); +} + +static int PTRFASTCALL +big2_isNmstrtMin(const ENCODING *enc, const char *p) +{ + return BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p); +} + +#undef VTABLE +#define VTABLE VTABLE1, big2_toUtf8, big2_toUtf16 + +#else /* not XML_MIN_SIZE */ + +#undef PREFIX +#define PREFIX(ident) big2_ ## ident +#define MINBPC(enc) 2 +/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */ +#define BYTE_TYPE(enc, p) BIG2_BYTE_TYPE(enc, p) +#define BYTE_TO_ASCII(enc, p) BIG2_BYTE_TO_ASCII(enc, p) +#define CHAR_MATCHES(enc, p, c) BIG2_CHAR_MATCHES(enc, p, c) +#define IS_NAME_CHAR(enc, p, n) 0 +#define IS_NAME_CHAR_MINBPC(enc, p) BIG2_IS_NAME_CHAR_MINBPC(enc, p) +#define IS_NMSTRT_CHAR(enc, p, n) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p) + +#include "xmltok_impl.c" + +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR + +#endif /* not XML_MIN_SIZE */ + +#ifdef XML_NS + +static const struct normal_encoding big2_encoding_ns = { + { VTABLE, 2, 0, +#if BYTEORDER == 4321 + 1 +#else + 0 +#endif + }, + { +#include "asciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) +}; + +#endif + +static const struct normal_encoding big2_encoding = { + { VTABLE, 2, 0, +#if BYTEORDER == 4321 + 1 +#else + 0 +#endif + }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) +}; + +#if BYTEORDER != 1234 + +#ifdef XML_NS + +static const struct normal_encoding internal_big2_encoding_ns = { + { VTABLE, 2, 0, 1 }, + { +#include "iasciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) +}; + +#endif + +static const struct normal_encoding internal_big2_encoding = { + { VTABLE, 2, 0, 1 }, + { +#define BT_COLON BT_NMSTRT +#include "iasciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) +}; + +#endif + +#undef PREFIX + +static int FASTCALL +streqci(const char *s1, const char *s2) +{ + for (;;) { + char c1 = *s1++; + char c2 = *s2++; + if (ASCII_a <= c1 && c1 <= ASCII_z) + c1 += ASCII_A - ASCII_a; + if (ASCII_a <= c2 && c2 <= ASCII_z) + c2 += ASCII_A - ASCII_a; + if (c1 != c2) + return 0; + if (!c1) + break; + } + return 1; +} + +static void PTRCALL +initUpdatePosition(const ENCODING *enc, const char *ptr, + const char *end, POSITION *pos) +{ + normal_updatePosition(&utf8_encoding.enc, ptr, end, pos); +} + +static int +toAscii(const ENCODING *enc, const char *ptr, const char *end) +{ + char buf[1]; + char *p = buf; + XmlUtf8Convert(enc, &ptr, end, &p, p + 1); + if (p == buf) + return -1; + else + return buf[0]; +} + +static int FASTCALL +isSpace(int c) +{ + switch (c) { + case 0x20: + case 0xD: + case 0xA: + case 0x9: + return 1; + } + return 0; +} + +/* Return 1 if there's just optional white space or there's an S + followed by name=val. +*/ +static int +parsePseudoAttribute(const ENCODING *enc, + const char *ptr, + const char *end, + const char **namePtr, + const char **nameEndPtr, + const char **valPtr, + const char **nextTokPtr) +{ + int c; + char open; + if (ptr == end) { + *namePtr = NULL; + return 1; + } + if (!isSpace(toAscii(enc, ptr, end))) { + *nextTokPtr = ptr; + return 0; + } + do { + ptr += enc->minBytesPerChar; + } while (isSpace(toAscii(enc, ptr, end))); + if (ptr == end) { + *namePtr = NULL; + return 1; + } + *namePtr = ptr; + for (;;) { + c = toAscii(enc, ptr, end); + if (c == -1) { + *nextTokPtr = ptr; + return 0; + } + if (c == ASCII_EQUALS) { + *nameEndPtr = ptr; + break; + } + if (isSpace(c)) { + *nameEndPtr = ptr; + do { + ptr += enc->minBytesPerChar; + } while (isSpace(c = toAscii(enc, ptr, end))); + if (c != ASCII_EQUALS) { + *nextTokPtr = ptr; + return 0; + } + break; + } + ptr += enc->minBytesPerChar; + } + if (ptr == *namePtr) { + *nextTokPtr = ptr; + return 0; + } + ptr += enc->minBytesPerChar; + c = toAscii(enc, ptr, end); + while (isSpace(c)) { + ptr += enc->minBytesPerChar; + c = toAscii(enc, ptr, end); + } + if (c != ASCII_QUOT && c != ASCII_APOS) { + *nextTokPtr = ptr; + return 0; + } + open = (char)c; + ptr += enc->minBytesPerChar; + *valPtr = ptr; + for (;; ptr += enc->minBytesPerChar) { + c = toAscii(enc, ptr, end); + if (c == open) + break; + if (!(ASCII_a <= c && c <= ASCII_z) + && !(ASCII_A <= c && c <= ASCII_Z) + && !(ASCII_0 <= c && c <= ASCII_9) + && c != ASCII_PERIOD + && c != ASCII_MINUS + && c != ASCII_UNDERSCORE) { + *nextTokPtr = ptr; + return 0; + } + } + *nextTokPtr = ptr + enc->minBytesPerChar; + return 1; +} + +static const char KW_version[] = { + ASCII_v, ASCII_e, ASCII_r, ASCII_s, ASCII_i, ASCII_o, ASCII_n, '\0' +}; + +static const char KW_encoding[] = { + ASCII_e, ASCII_n, ASCII_c, ASCII_o, ASCII_d, ASCII_i, ASCII_n, ASCII_g, '\0' +}; + +static const char KW_standalone[] = { + ASCII_s, ASCII_t, ASCII_a, ASCII_n, ASCII_d, ASCII_a, ASCII_l, ASCII_o, + ASCII_n, ASCII_e, '\0' +}; + +static const char KW_yes[] = { + ASCII_y, ASCII_e, ASCII_s, '\0' +}; + +static const char KW_no[] = { + ASCII_n, ASCII_o, '\0' +}; + +static int +doParseXmlDecl(const ENCODING *(*encodingFinder)(const ENCODING *, + const char *, + const char *), + int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **versionEndPtr, + const char **encodingName, + const ENCODING **encoding, + int *standalone) +{ + const char *val = NULL; + const char *name = NULL; + const char *nameEnd = NULL; + ptr += 5 * enc->minBytesPerChar; + end -= 2 * enc->minBytesPerChar; + if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr) + || !name) { + *badPtr = ptr; + return 0; + } + if (!XmlNameMatchesAscii(enc, name, nameEnd, KW_version)) { + if (!isGeneralTextEntity) { + *badPtr = name; + return 0; + } + } + else { + if (versionPtr) + *versionPtr = val; + if (versionEndPtr) + *versionEndPtr = ptr; + if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) { + *badPtr = ptr; + return 0; + } + if (!name) { + if (isGeneralTextEntity) { + /* a TextDecl must have an EncodingDecl */ + *badPtr = ptr; + return 0; + } + return 1; + } + } + if (XmlNameMatchesAscii(enc, name, nameEnd, KW_encoding)) { + int c = toAscii(enc, val, end); + if (!(ASCII_a <= c && c <= ASCII_z) && !(ASCII_A <= c && c <= ASCII_Z)) { + *badPtr = val; + return 0; + } + if (encodingName) + *encodingName = val; + if (encoding) + *encoding = encodingFinder(enc, val, ptr - enc->minBytesPerChar); + if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) { + *badPtr = ptr; + return 0; + } + if (!name) + return 1; + } + if (!XmlNameMatchesAscii(enc, name, nameEnd, KW_standalone) + || isGeneralTextEntity) { + *badPtr = name; + return 0; + } + if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_yes)) { + if (standalone) + *standalone = 1; + } + else if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_no)) { + if (standalone) + *standalone = 0; + } + else { + *badPtr = val; + return 0; + } + while (isSpace(toAscii(enc, ptr, end))) + ptr += enc->minBytesPerChar; + if (ptr != end) { + *badPtr = ptr; + return 0; + } + return 1; +} + +static int FASTCALL +checkCharRefNumber(int result) +{ + switch (result >> 8) { + case 0xD8: case 0xD9: case 0xDA: case 0xDB: + case 0xDC: case 0xDD: case 0xDE: case 0xDF: + return -1; + case 0: + if (latin1_encoding.type[result] == BT_NONXML) + return -1; + break; + case 0xFF: + if (result == 0xFFFE || result == 0xFFFF) + return -1; + break; + } + return result; +} + +int FASTCALL +XmlUtf8Encode(int c, char *buf) +{ + enum { + /* minN is minimum legal resulting value for N byte sequence */ + min2 = 0x80, + min3 = 0x800, + min4 = 0x10000 + }; + + if (c < 0) + return 0; + if (c < min2) { + buf[0] = (char)(c | UTF8_cval1); + return 1; + } + if (c < min3) { + buf[0] = (char)((c >> 6) | UTF8_cval2); + buf[1] = (char)((c & 0x3f) | 0x80); + return 2; + } + if (c < min4) { + buf[0] = (char)((c >> 12) | UTF8_cval3); + buf[1] = (char)(((c >> 6) & 0x3f) | 0x80); + buf[2] = (char)((c & 0x3f) | 0x80); + return 3; + } + if (c < 0x110000) { + buf[0] = (char)((c >> 18) | UTF8_cval4); + buf[1] = (char)(((c >> 12) & 0x3f) | 0x80); + buf[2] = (char)(((c >> 6) & 0x3f) | 0x80); + buf[3] = (char)((c & 0x3f) | 0x80); + return 4; + } + return 0; +} + +int FASTCALL +XmlUtf16Encode(int charNum, unsigned short *buf) +{ + if (charNum < 0) + return 0; + if (charNum < 0x10000) { + buf[0] = (unsigned short)charNum; + return 1; + } + if (charNum < 0x110000) { + charNum -= 0x10000; + buf[0] = (unsigned short)((charNum >> 10) + 0xD800); + buf[1] = (unsigned short)((charNum & 0x3FF) + 0xDC00); + return 2; + } + return 0; +} + +struct unknown_encoding { + struct normal_encoding normal; + CONVERTER convert; + void *userData; + unsigned short utf16[256]; + char utf8[256][4]; +}; + +#define AS_UNKNOWN_ENCODING(enc) ((const struct unknown_encoding *) (enc)) + +int +XmlSizeOfUnknownEncoding(void) +{ + return sizeof(struct unknown_encoding); +} + +static int PTRFASTCALL +unknown_isName(const ENCODING *enc, const char *p) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + int c = uenc->convert(uenc->userData, p); + if (c & ~0xFFFF) + return 0; + return UCS2_GET_NAMING(namePages, c >> 8, c & 0xFF); +} + +static int PTRFASTCALL +unknown_isNmstrt(const ENCODING *enc, const char *p) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + int c = uenc->convert(uenc->userData, p); + if (c & ~0xFFFF) + return 0; + return UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xFF); +} + +static int PTRFASTCALL +unknown_isInvalid(const ENCODING *enc, const char *p) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + int c = uenc->convert(uenc->userData, p); + return (c & ~0xFFFF) || checkCharRefNumber(c) < 0; +} + +static void PTRCALL +unknown_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + char buf[XML_UTF8_ENCODE_MAX]; + for (;;) { + const char *utf8; + int n; + if (*fromP == fromLim) + break; + utf8 = uenc->utf8[(unsigned char)**fromP]; + n = *utf8++; + if (n == 0) { + int c = uenc->convert(uenc->userData, *fromP); + n = XmlUtf8Encode(c, buf); + if (n > toLim - *toP) + break; + utf8 = buf; + *fromP += (AS_NORMAL_ENCODING(enc)->type[(unsigned char)**fromP] + - (BT_LEAD2 - 2)); + } + else { + if (n > toLim - *toP) + break; + (*fromP)++; + } + do { + *(*toP)++ = *utf8++; + } while (--n != 0); + } +} + +static void PTRCALL +unknown_toUtf16(const ENCODING *enc, + const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + while (*fromP != fromLim && *toP != toLim) { + unsigned short c = uenc->utf16[(unsigned char)**fromP]; + if (c == 0) { + c = (unsigned short) + uenc->convert(uenc->userData, *fromP); + *fromP += (AS_NORMAL_ENCODING(enc)->type[(unsigned char)**fromP] + - (BT_LEAD2 - 2)); + } + else + (*fromP)++; + *(*toP)++ = c; + } +} + +ENCODING * +XmlInitUnknownEncoding(void *mem, + int *table, + CONVERTER convert, + void *userData) +{ + int i; + struct unknown_encoding *e = (struct unknown_encoding *)mem; + for (i = 0; i < (int)sizeof(struct normal_encoding); i++) + ((char *)mem)[i] = ((char *)&latin1_encoding)[i]; + for (i = 0; i < 128; i++) + if (latin1_encoding.type[i] != BT_OTHER + && latin1_encoding.type[i] != BT_NONXML + && table[i] != i) + return 0; + for (i = 0; i < 256; i++) { + int c = table[i]; + if (c == -1) { + e->normal.type[i] = BT_MALFORM; + /* This shouldn't really get used. */ + e->utf16[i] = 0xFFFF; + e->utf8[i][0] = 1; + e->utf8[i][1] = 0; + } + else if (c < 0) { + if (c < -4) + return 0; + e->normal.type[i] = (unsigned char)(BT_LEAD2 - (c + 2)); + e->utf8[i][0] = 0; + e->utf16[i] = 0; + } + else if (c < 0x80) { + if (latin1_encoding.type[c] != BT_OTHER + && latin1_encoding.type[c] != BT_NONXML + && c != i) + return 0; + e->normal.type[i] = latin1_encoding.type[c]; + e->utf8[i][0] = 1; + e->utf8[i][1] = (char)c; + e->utf16[i] = (unsigned short)(c == 0 ? 0xFFFF : c); + } + else if (checkCharRefNumber(c) < 0) { + e->normal.type[i] = BT_NONXML; + /* This shouldn't really get used. */ + e->utf16[i] = 0xFFFF; + e->utf8[i][0] = 1; + e->utf8[i][1] = 0; + } + else { + if (c > 0xFFFF) + return 0; + if (UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xff)) + e->normal.type[i] = BT_NMSTRT; + else if (UCS2_GET_NAMING(namePages, c >> 8, c & 0xff)) + e->normal.type[i] = BT_NAME; + else + e->normal.type[i] = BT_OTHER; + e->utf8[i][0] = (char)XmlUtf8Encode(c, e->utf8[i] + 1); + e->utf16[i] = (unsigned short)c; + } + } + e->userData = userData; + e->convert = convert; + if (convert) { + e->normal.isName2 = unknown_isName; + e->normal.isName3 = unknown_isName; + e->normal.isName4 = unknown_isName; + e->normal.isNmstrt2 = unknown_isNmstrt; + e->normal.isNmstrt3 = unknown_isNmstrt; + e->normal.isNmstrt4 = unknown_isNmstrt; + e->normal.isInvalid2 = unknown_isInvalid; + e->normal.isInvalid3 = unknown_isInvalid; + e->normal.isInvalid4 = unknown_isInvalid; + } + e->normal.enc.utf8Convert = unknown_toUtf8; + e->normal.enc.utf16Convert = unknown_toUtf16; + return &(e->normal.enc); +} + +/* If this enumeration is changed, getEncodingIndex and encodings +must also be changed. */ +enum { + UNKNOWN_ENC = -1, + ISO_8859_1_ENC = 0, + US_ASCII_ENC, + UTF_8_ENC, + UTF_16_ENC, + UTF_16BE_ENC, + UTF_16LE_ENC, + /* must match encodingNames up to here */ + NO_ENC +}; + +static const char KW_ISO_8859_1[] = { + ASCII_I, ASCII_S, ASCII_O, ASCII_MINUS, ASCII_8, ASCII_8, ASCII_5, ASCII_9, + ASCII_MINUS, ASCII_1, '\0' +}; +static const char KW_US_ASCII[] = { + ASCII_U, ASCII_S, ASCII_MINUS, ASCII_A, ASCII_S, ASCII_C, ASCII_I, ASCII_I, + '\0' +}; +static const char KW_UTF_8[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_8, '\0' +}; +static const char KW_UTF_16[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, '\0' +}; +static const char KW_UTF_16BE[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, ASCII_B, ASCII_E, + '\0' +}; +static const char KW_UTF_16LE[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, ASCII_L, ASCII_E, + '\0' +}; + +static int FASTCALL +getEncodingIndex(const char *name) +{ + static const char * const encodingNames[] = { + KW_ISO_8859_1, + KW_US_ASCII, + KW_UTF_8, + KW_UTF_16, + KW_UTF_16BE, + KW_UTF_16LE, + }; + int i; + if (name == NULL) + return NO_ENC; + for (i = 0; i < (int)(sizeof(encodingNames)/sizeof(encodingNames[0])); i++) + if (streqci(name, encodingNames[i])) + return i; + return UNKNOWN_ENC; +} + +/* For binary compatibility, we store the index of the encoding + specified at initialization in the isUtf16 member. +*/ + +#define INIT_ENC_INDEX(enc) ((int)(enc)->initEnc.isUtf16) +#define SET_INIT_ENC_INDEX(enc, i) ((enc)->initEnc.isUtf16 = (char)i) + +/* This is what detects the encoding. encodingTable maps from + encoding indices to encodings; INIT_ENC_INDEX(enc) is the index of + the external (protocol) specified encoding; state is + XML_CONTENT_STATE if we're parsing an external text entity, and + XML_PROLOG_STATE otherwise. +*/ + + +static int +initScan(const ENCODING * const *encodingTable, + const INIT_ENCODING *enc, + int state, + const char *ptr, + const char *end, + const char **nextTokPtr) +{ + const ENCODING **encPtr; + + if (ptr == end) + return XML_TOK_NONE; + encPtr = enc->encPtr; + if (ptr + 1 == end) { + /* only a single byte available for auto-detection */ +#ifndef XML_DTD /* FIXME */ + /* a well-formed document entity must have more than one byte */ + if (state != XML_CONTENT_STATE) + return XML_TOK_PARTIAL; +#endif + /* so we're parsing an external text entity... */ + /* if UTF-16 was externally specified, then we need at least 2 bytes */ + switch (INIT_ENC_INDEX(enc)) { + case UTF_16_ENC: + case UTF_16LE_ENC: + case UTF_16BE_ENC: + return XML_TOK_PARTIAL; + } + switch ((unsigned char)*ptr) { + case 0xFE: + case 0xFF: + case 0xEF: /* possibly first byte of UTF-8 BOM */ + if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC + && state == XML_CONTENT_STATE) + break; + /* fall through */ + case 0x00: + case 0x3C: + return XML_TOK_PARTIAL; + } + } + else { + switch (((unsigned char)ptr[0] << 8) | (unsigned char)ptr[1]) { + case 0xFEFF: + if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC + && state == XML_CONTENT_STATE) + break; + *nextTokPtr = ptr + 2; + *encPtr = encodingTable[UTF_16BE_ENC]; + return XML_TOK_BOM; + /* 00 3C is handled in the default case */ + case 0x3C00: + if ((INIT_ENC_INDEX(enc) == UTF_16BE_ENC + || INIT_ENC_INDEX(enc) == UTF_16_ENC) + && state == XML_CONTENT_STATE) + break; + *encPtr = encodingTable[UTF_16LE_ENC]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); + case 0xFFFE: + if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC + && state == XML_CONTENT_STATE) + break; + *nextTokPtr = ptr + 2; + *encPtr = encodingTable[UTF_16LE_ENC]; + return XML_TOK_BOM; + case 0xEFBB: + /* Maybe a UTF-8 BOM (EF BB BF) */ + /* If there's an explicitly specified (external) encoding + of ISO-8859-1 or some flavour of UTF-16 + and this is an external text entity, + don't look for the BOM, + because it might be a legal data. + */ + if (state == XML_CONTENT_STATE) { + int e = INIT_ENC_INDEX(enc); + if (e == ISO_8859_1_ENC || e == UTF_16BE_ENC + || e == UTF_16LE_ENC || e == UTF_16_ENC) + break; + } + if (ptr + 2 == end) + return XML_TOK_PARTIAL; + if ((unsigned char)ptr[2] == 0xBF) { + *nextTokPtr = ptr + 3; + *encPtr = encodingTable[UTF_8_ENC]; + return XML_TOK_BOM; + } + break; + default: + if (ptr[0] == '\0') { + /* 0 isn't a legal data character. Furthermore a document + entity can only start with ASCII characters. So the only + way this can fail to be big-endian UTF-16 if it it's an + external parsed general entity that's labelled as + UTF-16LE. + */ + if (state == XML_CONTENT_STATE && INIT_ENC_INDEX(enc) == UTF_16LE_ENC) + break; + *encPtr = encodingTable[UTF_16BE_ENC]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); + } + else if (ptr[1] == '\0') { + /* We could recover here in the case: + - parsing an external entity + - second byte is 0 + - no externally specified encoding + - no encoding declaration + by assuming UTF-16LE. But we don't, because this would mean when + presented just with a single byte, we couldn't reliably determine + whether we needed further bytes. + */ + if (state == XML_CONTENT_STATE) + break; + *encPtr = encodingTable[UTF_16LE_ENC]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); + } + break; + } + } + *encPtr = encodingTable[INIT_ENC_INDEX(enc)]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); +} + + +#define NS(x) x +#define ns(x) x +#include "xmltok_ns.c" +#undef NS +#undef ns + +#ifdef XML_NS + +#define NS(x) x ## NS +#define ns(x) x ## _ns + +#include "xmltok_ns.c" + +#undef NS +#undef ns + +ENCODING * +XmlInitUnknownEncodingNS(void *mem, + int *table, + CONVERTER convert, + void *userData) +{ + ENCODING *enc = XmlInitUnknownEncoding(mem, table, convert, userData); + if (enc) + ((struct normal_encoding *)enc)->type[ASCII_COLON] = BT_COLON; + return enc; +} + +#endif /* XML_NS */ diff --git a/src/lib/expat/xmltok.h b/src/lib/expat/xmltok.h new file mode 100644 index 00000000000..ca867aa6b42 --- /dev/null +++ b/src/lib/expat/xmltok.h @@ -0,0 +1,316 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#ifndef XmlTok_INCLUDED +#define XmlTok_INCLUDED 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* The following token may be returned by XmlContentTok */ +#define XML_TOK_TRAILING_RSQB -5 /* ] or ]] at the end of the scan; might be + start of illegal ]]> sequence */ +/* The following tokens may be returned by both XmlPrologTok and + XmlContentTok. +*/ +#define XML_TOK_NONE -4 /* The string to be scanned is empty */ +#define XML_TOK_TRAILING_CR -3 /* A CR at the end of the scan; + might be part of CRLF sequence */ +#define XML_TOK_PARTIAL_CHAR -2 /* only part of a multibyte sequence */ +#define XML_TOK_PARTIAL -1 /* only part of a token */ +#define XML_TOK_INVALID 0 + +/* The following tokens are returned by XmlContentTok; some are also + returned by XmlAttributeValueTok, XmlEntityTok, XmlCdataSectionTok. +*/ +#define XML_TOK_START_TAG_WITH_ATTS 1 +#define XML_TOK_START_TAG_NO_ATTS 2 +#define XML_TOK_EMPTY_ELEMENT_WITH_ATTS 3 /* empty element tag <e/> */ +#define XML_TOK_EMPTY_ELEMENT_NO_ATTS 4 +#define XML_TOK_END_TAG 5 +#define XML_TOK_DATA_CHARS 6 +#define XML_TOK_DATA_NEWLINE 7 +#define XML_TOK_CDATA_SECT_OPEN 8 +#define XML_TOK_ENTITY_REF 9 +#define XML_TOK_CHAR_REF 10 /* numeric character reference */ + +/* The following tokens may be returned by both XmlPrologTok and + XmlContentTok. +*/ +#define XML_TOK_PI 11 /* processing instruction */ +#define XML_TOK_XML_DECL 12 /* XML decl or text decl */ +#define XML_TOK_COMMENT 13 +#define XML_TOK_BOM 14 /* Byte order mark */ + +/* The following tokens are returned only by XmlPrologTok */ +#define XML_TOK_PROLOG_S 15 +#define XML_TOK_DECL_OPEN 16 /* <!foo */ +#define XML_TOK_DECL_CLOSE 17 /* > */ +#define XML_TOK_NAME 18 +#define XML_TOK_NMTOKEN 19 +#define XML_TOK_POUND_NAME 20 /* #name */ +#define XML_TOK_OR 21 /* | */ +#define XML_TOK_PERCENT 22 +#define XML_TOK_OPEN_PAREN 23 +#define XML_TOK_CLOSE_PAREN 24 +#define XML_TOK_OPEN_BRACKET 25 +#define XML_TOK_CLOSE_BRACKET 26 +#define XML_TOK_LITERAL 27 +#define XML_TOK_PARAM_ENTITY_REF 28 +#define XML_TOK_INSTANCE_START 29 + +/* The following occur only in element type declarations */ +#define XML_TOK_NAME_QUESTION 30 /* name? */ +#define XML_TOK_NAME_ASTERISK 31 /* name* */ +#define XML_TOK_NAME_PLUS 32 /* name+ */ +#define XML_TOK_COND_SECT_OPEN 33 /* <![ */ +#define XML_TOK_COND_SECT_CLOSE 34 /* ]]> */ +#define XML_TOK_CLOSE_PAREN_QUESTION 35 /* )? */ +#define XML_TOK_CLOSE_PAREN_ASTERISK 36 /* )* */ +#define XML_TOK_CLOSE_PAREN_PLUS 37 /* )+ */ +#define XML_TOK_COMMA 38 + +/* The following token is returned only by XmlAttributeValueTok */ +#define XML_TOK_ATTRIBUTE_VALUE_S 39 + +/* The following token is returned only by XmlCdataSectionTok */ +#define XML_TOK_CDATA_SECT_CLOSE 40 + +/* With namespace processing this is returned by XmlPrologTok for a + name with a colon. +*/ +#define XML_TOK_PREFIXED_NAME 41 + +#ifdef XML_DTD +#define XML_TOK_IGNORE_SECT 42 +#endif /* XML_DTD */ + +#ifdef XML_DTD +#define XML_N_STATES 4 +#else /* not XML_DTD */ +#define XML_N_STATES 3 +#endif /* not XML_DTD */ + +#define XML_PROLOG_STATE 0 +#define XML_CONTENT_STATE 1 +#define XML_CDATA_SECTION_STATE 2 +#ifdef XML_DTD +#define XML_IGNORE_SECTION_STATE 3 +#endif /* XML_DTD */ + +#define XML_N_LITERAL_TYPES 2 +#define XML_ATTRIBUTE_VALUE_LITERAL 0 +#define XML_ENTITY_VALUE_LITERAL 1 + +/* The size of the buffer passed to XmlUtf8Encode must be at least this. */ +#define XML_UTF8_ENCODE_MAX 4 +/* The size of the buffer passed to XmlUtf16Encode must be at least this. */ +#define XML_UTF16_ENCODE_MAX 2 + +typedef struct position { + /* first line and first column are 0 not 1 */ + XML_Size lineNumber; + XML_Size columnNumber; +} POSITION; + +typedef struct { + const char *name; + const char *valuePtr; + const char *valueEnd; + char normalized; +} ATTRIBUTE; + +struct encoding; +typedef struct encoding ENCODING; + +typedef int (PTRCALL *SCANNER)(const ENCODING *, + const char *, + const char *, + const char **); + +struct encoding { + SCANNER scanners[XML_N_STATES]; + SCANNER literalScanners[XML_N_LITERAL_TYPES]; + int (PTRCALL *sameName)(const ENCODING *, + const char *, + const char *); + int (PTRCALL *nameMatchesAscii)(const ENCODING *, + const char *, + const char *, + const char *); + int (PTRFASTCALL *nameLength)(const ENCODING *, const char *); + const char *(PTRFASTCALL *skipS)(const ENCODING *, const char *); + int (PTRCALL *getAtts)(const ENCODING *enc, + const char *ptr, + int attsMax, + ATTRIBUTE *atts); + int (PTRFASTCALL *charRefNumber)(const ENCODING *enc, const char *ptr); + int (PTRCALL *predefinedEntityName)(const ENCODING *, + const char *, + const char *); + void (PTRCALL *updatePosition)(const ENCODING *, + const char *ptr, + const char *end, + POSITION *); + int (PTRCALL *isPublicId)(const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr); + void (PTRCALL *utf8Convert)(const ENCODING *enc, + const char **fromP, + const char *fromLim, + char **toP, + const char *toLim); + void (PTRCALL *utf16Convert)(const ENCODING *enc, + const char **fromP, + const char *fromLim, + unsigned short **toP, + const unsigned short *toLim); + int minBytesPerChar; + char isUtf8; + char isUtf16; +}; + +/* Scan the string starting at ptr until the end of the next complete + token, but do not scan past eptr. Return an integer giving the + type of token. + + Return XML_TOK_NONE when ptr == eptr; nextTokPtr will not be set. + + Return XML_TOK_PARTIAL when the string does not contain a complete + token; nextTokPtr will not be set. + + Return XML_TOK_INVALID when the string does not start a valid + token; nextTokPtr will be set to point to the character which made + the token invalid. + + Otherwise the string starts with a valid token; nextTokPtr will be + set to point to the character following the end of that token. + + Each data character counts as a single token, but adjacent data + characters may be returned together. Similarly for characters in + the prolog outside literals, comments and processing instructions. +*/ + + +#define XmlTok(enc, state, ptr, end, nextTokPtr) \ + (((enc)->scanners[state])(enc, ptr, end, nextTokPtr)) + +#define XmlPrologTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_PROLOG_STATE, ptr, end, nextTokPtr) + +#define XmlContentTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_CONTENT_STATE, ptr, end, nextTokPtr) + +#define XmlCdataSectionTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_CDATA_SECTION_STATE, ptr, end, nextTokPtr) + +#ifdef XML_DTD + +#define XmlIgnoreSectionTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_IGNORE_SECTION_STATE, ptr, end, nextTokPtr) + +#endif /* XML_DTD */ + +/* This is used for performing a 2nd-level tokenization on the content + of a literal that has already been returned by XmlTok. +*/ +#define XmlLiteralTok(enc, literalType, ptr, end, nextTokPtr) \ + (((enc)->literalScanners[literalType])(enc, ptr, end, nextTokPtr)) + +#define XmlAttributeValueTok(enc, ptr, end, nextTokPtr) \ + XmlLiteralTok(enc, XML_ATTRIBUTE_VALUE_LITERAL, ptr, end, nextTokPtr) + +#define XmlEntityValueTok(enc, ptr, end, nextTokPtr) \ + XmlLiteralTok(enc, XML_ENTITY_VALUE_LITERAL, ptr, end, nextTokPtr) + +#define XmlSameName(enc, ptr1, ptr2) (((enc)->sameName)(enc, ptr1, ptr2)) + +#define XmlNameMatchesAscii(enc, ptr1, end1, ptr2) \ + (((enc)->nameMatchesAscii)(enc, ptr1, end1, ptr2)) + +#define XmlNameLength(enc, ptr) \ + (((enc)->nameLength)(enc, ptr)) + +#define XmlSkipS(enc, ptr) \ + (((enc)->skipS)(enc, ptr)) + +#define XmlGetAttributes(enc, ptr, attsMax, atts) \ + (((enc)->getAtts)(enc, ptr, attsMax, atts)) + +#define XmlCharRefNumber(enc, ptr) \ + (((enc)->charRefNumber)(enc, ptr)) + +#define XmlPredefinedEntityName(enc, ptr, end) \ + (((enc)->predefinedEntityName)(enc, ptr, end)) + +#define XmlUpdatePosition(enc, ptr, end, pos) \ + (((enc)->updatePosition)(enc, ptr, end, pos)) + +#define XmlIsPublicId(enc, ptr, end, badPtr) \ + (((enc)->isPublicId)(enc, ptr, end, badPtr)) + +#define XmlUtf8Convert(enc, fromP, fromLim, toP, toLim) \ + (((enc)->utf8Convert)(enc, fromP, fromLim, toP, toLim)) + +#define XmlUtf16Convert(enc, fromP, fromLim, toP, toLim) \ + (((enc)->utf16Convert)(enc, fromP, fromLim, toP, toLim)) + +typedef struct { + ENCODING initEnc; + const ENCODING **encPtr; +} INIT_ENCODING; + +int XmlParseXmlDecl(int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **versionEndPtr, + const char **encodingNamePtr, + const ENCODING **namedEncodingPtr, + int *standalonePtr); + +int XmlInitEncoding(INIT_ENCODING *, const ENCODING **, const char *name); +const ENCODING *XmlGetUtf8InternalEncoding(void); +const ENCODING *XmlGetUtf16InternalEncoding(void); +int FASTCALL XmlUtf8Encode(int charNumber, char *buf); +int FASTCALL XmlUtf16Encode(int charNumber, unsigned short *buf); +int XmlSizeOfUnknownEncoding(void); + + +typedef int (XMLCALL *CONVERTER) (void *userData, const char *p); + +ENCODING * +XmlInitUnknownEncoding(void *mem, + int *table, + CONVERTER convert, + void *userData); + +int XmlParseXmlDeclNS(int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **versionEndPtr, + const char **encodingNamePtr, + const ENCODING **namedEncodingPtr, + int *standalonePtr); + +int XmlInitEncodingNS(INIT_ENCODING *, const ENCODING **, const char *name); +const ENCODING *XmlGetUtf8InternalEncodingNS(void); +const ENCODING *XmlGetUtf16InternalEncodingNS(void); +ENCODING * +XmlInitUnknownEncodingNS(void *mem, + int *table, + CONVERTER convert, + void *userData); +#ifdef __cplusplus +} +#endif + +#endif /* not XmlTok_INCLUDED */ diff --git a/src/lib/expat/xmltok_impl.c b/src/lib/expat/xmltok_impl.c new file mode 100644 index 00000000000..0ee57abb1f4 --- /dev/null +++ b/src/lib/expat/xmltok_impl.c @@ -0,0 +1,1779 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#ifndef IS_INVALID_CHAR +#define IS_INVALID_CHAR(enc, ptr, n) (0) +#endif + +#define INVALID_LEAD_CASE(n, ptr, nextTokPtr) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (IS_INVALID_CHAR(enc, ptr, n)) { \ + *(nextTokPtr) = (ptr); \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +#define INVALID_CASES(ptr, nextTokPtr) \ + INVALID_LEAD_CASE(2, ptr, nextTokPtr) \ + INVALID_LEAD_CASE(3, ptr, nextTokPtr) \ + INVALID_LEAD_CASE(4, ptr, nextTokPtr) \ + case BT_NONXML: \ + case BT_MALFORM: \ + case BT_TRAIL: \ + *(nextTokPtr) = (ptr); \ + return XML_TOK_INVALID; + +#define CHECK_NAME_CASE(n, enc, ptr, end, nextTokPtr) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (!IS_NAME_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +#define CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) \ + case BT_NONASCII: \ + if (!IS_NAME_CHAR_MINBPC(enc, ptr)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + case BT_NMSTRT: \ + case BT_HEX: \ + case BT_DIGIT: \ + case BT_NAME: \ + case BT_MINUS: \ + ptr += MINBPC(enc); \ + break; \ + CHECK_NAME_CASE(2, enc, ptr, end, nextTokPtr) \ + CHECK_NAME_CASE(3, enc, ptr, end, nextTokPtr) \ + CHECK_NAME_CASE(4, enc, ptr, end, nextTokPtr) + +#define CHECK_NMSTRT_CASE(n, enc, ptr, end, nextTokPtr) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (!IS_NMSTRT_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +#define CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) \ + case BT_NONASCII: \ + if (!IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + case BT_NMSTRT: \ + case BT_HEX: \ + ptr += MINBPC(enc); \ + break; \ + CHECK_NMSTRT_CASE(2, enc, ptr, end, nextTokPtr) \ + CHECK_NMSTRT_CASE(3, enc, ptr, end, nextTokPtr) \ + CHECK_NMSTRT_CASE(4, enc, ptr, end, nextTokPtr) + +#ifndef PREFIX +#define PREFIX(ident) ident +#endif + +/* ptr points to character following "<!-" */ + +static int PTRCALL +PREFIX(scanComment)(const ENCODING *enc, const char *ptr, + const char *end, const char **nextTokPtr) +{ + if (ptr != end) { + if (!CHAR_MATCHES(enc, ptr, ASCII_MINUS)) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + ptr += MINBPC(enc); + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + INVALID_CASES(ptr, nextTokPtr) + case BT_MINUS: + if ((ptr += MINBPC(enc)) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, ASCII_MINUS)) { + if ((ptr += MINBPC(enc)) == end) + return XML_TOK_PARTIAL; + if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_COMMENT; + } + break; + default: + ptr += MINBPC(enc); + break; + } + } + } + return XML_TOK_PARTIAL; +} + +/* ptr points to character following "<!" */ + +static int PTRCALL +PREFIX(scanDecl)(const ENCODING *enc, const char *ptr, + const char *end, const char **nextTokPtr) +{ + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + case BT_MINUS: + return PREFIX(scanComment)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_LSQB: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_COND_SECT_OPEN; + case BT_NMSTRT: + case BT_HEX: + ptr += MINBPC(enc); + break; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_PERCNT: + if (ptr + MINBPC(enc) == end) + return XML_TOK_PARTIAL; + /* don't allow <!ENTITY% foo "whatever"> */ + switch (BYTE_TYPE(enc, ptr + MINBPC(enc))) { + case BT_S: case BT_CR: case BT_LF: case BT_PERCNT: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + /* fall through */ + case BT_S: case BT_CR: case BT_LF: + *nextTokPtr = ptr; + return XML_TOK_DECL_OPEN; + case BT_NMSTRT: + case BT_HEX: + ptr += MINBPC(enc); + break; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +static int PTRCALL +PREFIX(checkPiTarget)(const ENCODING *enc, const char *ptr, + const char *end, int *tokPtr) +{ + int upper = 0; + *tokPtr = XML_TOK_PI; + if (end - ptr != MINBPC(enc)*3) + return 1; + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_x: + break; + case ASCII_X: + upper = 1; + break; + default: + return 1; + } + ptr += MINBPC(enc); + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_m: + break; + case ASCII_M: + upper = 1; + break; + default: + return 1; + } + ptr += MINBPC(enc); + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_l: + break; + case ASCII_L: + upper = 1; + break; + default: + return 1; + } + if (upper) + return 0; + *tokPtr = XML_TOK_XML_DECL; + return 1; +} + +/* ptr points to character following "<?" */ + +static int PTRCALL +PREFIX(scanPi)(const ENCODING *enc, const char *ptr, + const char *end, const char **nextTokPtr) +{ + int tok; + const char *target = ptr; + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_S: case BT_CR: case BT_LF: + if (!PREFIX(checkPiTarget)(enc, target, ptr, &tok)) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + ptr += MINBPC(enc); + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + INVALID_CASES(ptr, nextTokPtr) + case BT_QUEST: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, ASCII_GT)) { + *nextTokPtr = ptr + MINBPC(enc); + return tok; + } + break; + default: + ptr += MINBPC(enc); + break; + } + } + return XML_TOK_PARTIAL; + case BT_QUEST: + if (!PREFIX(checkPiTarget)(enc, target, ptr, &tok)) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, ASCII_GT)) { + *nextTokPtr = ptr + MINBPC(enc); + return tok; + } + /* fall through */ + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +static int PTRCALL +PREFIX(scanCdataSection)(const ENCODING *enc, const char *ptr, + const char *end, const char **nextTokPtr) +{ + static const char CDATA_LSQB[] = { ASCII_C, ASCII_D, ASCII_A, + ASCII_T, ASCII_A, ASCII_LSQB }; + int i; + /* CDATA[ */ + if (end - ptr < 6 * MINBPC(enc)) + return XML_TOK_PARTIAL; + for (i = 0; i < 6; i++, ptr += MINBPC(enc)) { + if (!CHAR_MATCHES(enc, ptr, CDATA_LSQB[i])) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + *nextTokPtr = ptr; + return XML_TOK_CDATA_SECT_OPEN; +} + +static int PTRCALL +PREFIX(cdataSectionTok)(const ENCODING *enc, const char *ptr, + const char *end, const char **nextTokPtr) +{ + if (ptr == end) + return XML_TOK_NONE; + if (MINBPC(enc) > 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + if (n == 0) + return XML_TOK_PARTIAL; + end = ptr + n; + } + } + switch (BYTE_TYPE(enc, ptr)) { + case BT_RSQB: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + if (!CHAR_MATCHES(enc, ptr, ASCII_RSQB)) + break; + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) { + ptr -= MINBPC(enc); + break; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CDATA_SECT_CLOSE; + case BT_CR: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + case BT_LF: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + INVALID_CASES(ptr, nextTokPtr) + default: + ptr += MINBPC(enc); + break; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_DATA_CHARS; \ + } \ + ptr += n; \ + break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NONXML: + case BT_MALFORM: + case BT_TRAIL: + case BT_CR: + case BT_LF: + case BT_RSQB: + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +/* ptr points to character following "</" */ + +static int PTRCALL +PREFIX(scanEndTag)(const ENCODING *enc, const char *ptr, + const char *end, const char **nextTokPtr) +{ + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_S: case BT_CR: case BT_LF: + for (ptr += MINBPC(enc); ptr != end; ptr += MINBPC(enc)) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_S: case BT_CR: case BT_LF: + break; + case BT_GT: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_END_TAG; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +#ifdef XML_NS + case BT_COLON: + /* no need to check qname syntax here, + since end-tag must match exactly */ + ptr += MINBPC(enc); + break; +#endif + case BT_GT: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_END_TAG; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +/* ptr points to character following "&#X" */ + +static int PTRCALL +PREFIX(scanHexCharRef)(const ENCODING *enc, const char *ptr, + const char *end, const char **nextTokPtr) +{ + if (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_DIGIT: + case BT_HEX: + break; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + for (ptr += MINBPC(enc); ptr != end; ptr += MINBPC(enc)) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_DIGIT: + case BT_HEX: + break; + case BT_SEMI: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CHAR_REF; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + } + return XML_TOK_PARTIAL; +} + +/* ptr points to character following "&#" */ + +static int PTRCALL +PREFIX(scanCharRef)(const ENCODING *enc, const char *ptr, + const char *end, const char **nextTokPtr) +{ + if (ptr != end) { + if (CHAR_MATCHES(enc, ptr, ASCII_x)) + return PREFIX(scanHexCharRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + switch (BYTE_TYPE(enc, ptr)) { + case BT_DIGIT: + break; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + for (ptr += MINBPC(enc); ptr != end; ptr += MINBPC(enc)) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_DIGIT: + break; + case BT_SEMI: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CHAR_REF; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + } + return XML_TOK_PARTIAL; +} + +/* ptr points to character following "&" */ + +static int PTRCALL +PREFIX(scanRef)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + case BT_NUM: + return PREFIX(scanCharRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_SEMI: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_ENTITY_REF; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +/* ptr points to character following first character of attribute name */ + +static int PTRCALL +PREFIX(scanAtts)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ +#ifdef XML_NS + int hadColon = 0; +#endif + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) +#ifdef XML_NS + case BT_COLON: + if (hadColon) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + hadColon = 1; + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + break; +#endif + case BT_S: case BT_CR: case BT_LF: + for (;;) { + int t; + + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + t = BYTE_TYPE(enc, ptr); + if (t == BT_EQUALS) + break; + switch (t) { + case BT_S: + case BT_LF: + case BT_CR: + break; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + /* fall through */ + case BT_EQUALS: + { + int open; +#ifdef XML_NS + hadColon = 0; +#endif + for (;;) { + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + open = BYTE_TYPE(enc, ptr); + if (open == BT_QUOT || open == BT_APOS) + break; + switch (open) { + case BT_S: + case BT_LF: + case BT_CR: + break; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + ptr += MINBPC(enc); + /* in attribute value */ + for (;;) { + int t; + if (ptr == end) + return XML_TOK_PARTIAL; + t = BYTE_TYPE(enc, ptr); + if (t == open) + break; + switch (t) { + INVALID_CASES(ptr, nextTokPtr) + case BT_AMP: + { + int tok = PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, &ptr); + if (tok <= 0) { + if (tok == XML_TOK_INVALID) + *nextTokPtr = ptr; + return tok; + } + break; + } + case BT_LT: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + default: + ptr += MINBPC(enc); + break; + } + } + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + case BT_S: + case BT_CR: + case BT_LF: + break; + case BT_SOL: + goto sol; + case BT_GT: + goto gt; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + /* ptr points to closing quote */ + for (;;) { + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + case BT_S: case BT_CR: case BT_LF: + continue; + case BT_GT: + gt: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_START_TAG_WITH_ATTS; + case BT_SOL: + sol: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_EMPTY_ELEMENT_WITH_ATTS; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + break; + } + break; + } + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +/* ptr points to character following "<" */ + +static int PTRCALL +PREFIX(scanLt)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ +#ifdef XML_NS + int hadColon; +#endif + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + case BT_EXCL: + if ((ptr += MINBPC(enc)) == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + case BT_MINUS: + return PREFIX(scanComment)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_LSQB: + return PREFIX(scanCdataSection)(enc, ptr + MINBPC(enc), + end, nextTokPtr); + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + case BT_QUEST: + return PREFIX(scanPi)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_SOL: + return PREFIX(scanEndTag)(enc, ptr + MINBPC(enc), end, nextTokPtr); + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } +#ifdef XML_NS + hadColon = 0; +#endif + /* we have a start-tag */ + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) +#ifdef XML_NS + case BT_COLON: + if (hadColon) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + hadColon = 1; + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + break; +#endif + case BT_S: case BT_CR: case BT_LF: + { + ptr += MINBPC(enc); + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + case BT_GT: + goto gt; + case BT_SOL: + goto sol; + case BT_S: case BT_CR: case BT_LF: + ptr += MINBPC(enc); + continue; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + return PREFIX(scanAtts)(enc, ptr, end, nextTokPtr); + } + return XML_TOK_PARTIAL; + } + case BT_GT: + gt: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_START_TAG_NO_ATTS; + case BT_SOL: + sol: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_EMPTY_ELEMENT_NO_ATTS; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +static int PTRCALL +PREFIX(contentTok)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + if (ptr == end) + return XML_TOK_NONE; + if (MINBPC(enc) > 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + if (n == 0) + return XML_TOK_PARTIAL; + end = ptr + n; + } + } + switch (BYTE_TYPE(enc, ptr)) { + case BT_LT: + return PREFIX(scanLt)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_AMP: + return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_CR: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_CR; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + case BT_LF: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + case BT_RSQB: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_RSQB; + if (!CHAR_MATCHES(enc, ptr, ASCII_RSQB)) + break; + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_RSQB; + if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) { + ptr -= MINBPC(enc); + break; + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + INVALID_CASES(ptr, nextTokPtr) + default: + ptr += MINBPC(enc); + break; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_DATA_CHARS; \ + } \ + ptr += n; \ + break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_RSQB: + if (ptr + MINBPC(enc) != end) { + if (!CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_RSQB)) { + ptr += MINBPC(enc); + break; + } + if (ptr + 2*MINBPC(enc) != end) { + if (!CHAR_MATCHES(enc, ptr + 2*MINBPC(enc), ASCII_GT)) { + ptr += MINBPC(enc); + break; + } + *nextTokPtr = ptr + 2*MINBPC(enc); + return XML_TOK_INVALID; + } + } + /* fall through */ + case BT_AMP: + case BT_LT: + case BT_NONXML: + case BT_MALFORM: + case BT_TRAIL: + case BT_CR: + case BT_LF: + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +/* ptr points to character following "%" */ + +static int PTRCALL +PREFIX(scanPercent)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + if (ptr == end) + return -XML_TOK_PERCENT; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + case BT_S: case BT_LF: case BT_CR: case BT_PERCNT: + *nextTokPtr = ptr; + return XML_TOK_PERCENT; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_SEMI: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_PARAM_ENTITY_REF; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +static int PTRCALL +PREFIX(scanPoundName)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_CR: case BT_LF: case BT_S: + case BT_RPAR: case BT_GT: case BT_PERCNT: case BT_VERBAR: + *nextTokPtr = ptr; + return XML_TOK_POUND_NAME; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return -XML_TOK_POUND_NAME; +} + +static int PTRCALL +PREFIX(scanLit)(int open, const ENCODING *enc, + const char *ptr, const char *end, + const char **nextTokPtr) +{ + while (ptr != end) { + int t = BYTE_TYPE(enc, ptr); + switch (t) { + INVALID_CASES(ptr, nextTokPtr) + case BT_QUOT: + case BT_APOS: + ptr += MINBPC(enc); + if (t != open) + break; + if (ptr == end) + return -XML_TOK_LITERAL; + *nextTokPtr = ptr; + switch (BYTE_TYPE(enc, ptr)) { + case BT_S: case BT_CR: case BT_LF: + case BT_GT: case BT_PERCNT: case BT_LSQB: + return XML_TOK_LITERAL; + default: + return XML_TOK_INVALID; + } + default: + ptr += MINBPC(enc); + break; + } + } + return XML_TOK_PARTIAL; +} + +static int PTRCALL +PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + int tok; + if (ptr == end) + return XML_TOK_NONE; + if (MINBPC(enc) > 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + if (n == 0) + return XML_TOK_PARTIAL; + end = ptr + n; + } + } + switch (BYTE_TYPE(enc, ptr)) { + case BT_QUOT: + return PREFIX(scanLit)(BT_QUOT, enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_APOS: + return PREFIX(scanLit)(BT_APOS, enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_LT: + { + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + case BT_EXCL: + return PREFIX(scanDecl)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_QUEST: + return PREFIX(scanPi)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_NMSTRT: + case BT_HEX: + case BT_NONASCII: + case BT_LEAD2: + case BT_LEAD3: + case BT_LEAD4: + *nextTokPtr = ptr - MINBPC(enc); + return XML_TOK_INSTANCE_START; + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + case BT_CR: + if (ptr + MINBPC(enc) == end) { + *nextTokPtr = end; + /* indicate that this might be part of a CR/LF pair */ + return -XML_TOK_PROLOG_S; + } + /* fall through */ + case BT_S: case BT_LF: + for (;;) { + ptr += MINBPC(enc); + if (ptr == end) + break; + switch (BYTE_TYPE(enc, ptr)) { + case BT_S: case BT_LF: + break; + case BT_CR: + /* don't split CR/LF pair */ + if (ptr + MINBPC(enc) != end) + break; + /* fall through */ + default: + *nextTokPtr = ptr; + return XML_TOK_PROLOG_S; + } + } + *nextTokPtr = ptr; + return XML_TOK_PROLOG_S; + case BT_PERCNT: + return PREFIX(scanPercent)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_COMMA: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_COMMA; + case BT_LSQB: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_OPEN_BRACKET; + case BT_RSQB: + ptr += MINBPC(enc); + if (ptr == end) + return -XML_TOK_CLOSE_BRACKET; + if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) { + if (ptr + MINBPC(enc) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_GT)) { + *nextTokPtr = ptr + 2*MINBPC(enc); + return XML_TOK_COND_SECT_CLOSE; + } + } + *nextTokPtr = ptr; + return XML_TOK_CLOSE_BRACKET; + case BT_LPAR: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_OPEN_PAREN; + case BT_RPAR: + ptr += MINBPC(enc); + if (ptr == end) + return -XML_TOK_CLOSE_PAREN; + switch (BYTE_TYPE(enc, ptr)) { + case BT_AST: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CLOSE_PAREN_ASTERISK; + case BT_QUEST: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CLOSE_PAREN_QUESTION; + case BT_PLUS: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CLOSE_PAREN_PLUS; + case BT_CR: case BT_LF: case BT_S: + case BT_GT: case BT_COMMA: case BT_VERBAR: + case BT_RPAR: + *nextTokPtr = ptr; + return XML_TOK_CLOSE_PAREN; + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + case BT_VERBAR: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_OR; + case BT_GT: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DECL_CLOSE; + case BT_NUM: + return PREFIX(scanPoundName)(enc, ptr + MINBPC(enc), end, nextTokPtr); +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (IS_NMSTRT_CHAR(enc, ptr, n)) { \ + ptr += n; \ + tok = XML_TOK_NAME; \ + break; \ + } \ + if (IS_NAME_CHAR(enc, ptr, n)) { \ + ptr += n; \ + tok = XML_TOK_NMTOKEN; \ + break; \ + } \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NMSTRT: + case BT_HEX: + tok = XML_TOK_NAME; + ptr += MINBPC(enc); + break; + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: +#ifdef XML_NS + case BT_COLON: +#endif + tok = XML_TOK_NMTOKEN; + ptr += MINBPC(enc); + break; + case BT_NONASCII: + if (IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { + ptr += MINBPC(enc); + tok = XML_TOK_NAME; + break; + } + if (IS_NAME_CHAR_MINBPC(enc, ptr)) { + ptr += MINBPC(enc); + tok = XML_TOK_NMTOKEN; + break; + } + /* fall through */ + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_GT: case BT_RPAR: case BT_COMMA: + case BT_VERBAR: case BT_LSQB: case BT_PERCNT: + case BT_S: case BT_CR: case BT_LF: + *nextTokPtr = ptr; + return tok; +#ifdef XML_NS + case BT_COLON: + ptr += MINBPC(enc); + switch (tok) { + case XML_TOK_NAME: + if (ptr == end) + return XML_TOK_PARTIAL; + tok = XML_TOK_PREFIXED_NAME; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + default: + tok = XML_TOK_NMTOKEN; + break; + } + break; + case XML_TOK_PREFIXED_NAME: + tok = XML_TOK_NMTOKEN; + break; + } + break; +#endif + case BT_PLUS: + if (tok == XML_TOK_NMTOKEN) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_NAME_PLUS; + case BT_AST: + if (tok == XML_TOK_NMTOKEN) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_NAME_ASTERISK; + case BT_QUEST: + if (tok == XML_TOK_NMTOKEN) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_NAME_QUESTION; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return -tok; +} + +static int PTRCALL +PREFIX(attributeValueTok)(const ENCODING *enc, const char *ptr, + const char *end, const char **nextTokPtr) +{ + const char *start; + if (ptr == end) + return XML_TOK_NONE; + start = ptr; + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: ptr += n; break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_AMP: + if (ptr == start) + return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_LT: + /* this is for inside entity references */ + *nextTokPtr = ptr; + return XML_TOK_INVALID; + case BT_LF: + if (ptr == start) { + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_CR: + if (ptr == start) { + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_CR; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_S: + if (ptr == start) { + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_ATTRIBUTE_VALUE_S; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +static int PTRCALL +PREFIX(entityValueTok)(const ENCODING *enc, const char *ptr, + const char *end, const char **nextTokPtr) +{ + const char *start; + if (ptr == end) + return XML_TOK_NONE; + start = ptr; + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: ptr += n; break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_AMP: + if (ptr == start) + return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_PERCNT: + if (ptr == start) { + int tok = PREFIX(scanPercent)(enc, ptr + MINBPC(enc), + end, nextTokPtr); + return (tok == XML_TOK_PERCENT) ? XML_TOK_INVALID : tok; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_LF: + if (ptr == start) { + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_CR: + if (ptr == start) { + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_CR; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +#ifdef XML_DTD + +static int PTRCALL +PREFIX(ignoreSectionTok)(const ENCODING *enc, const char *ptr, + const char *end, const char **nextTokPtr) +{ + int level = 0; + if (MINBPC(enc) > 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + end = ptr + n; + } + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + INVALID_CASES(ptr, nextTokPtr) + case BT_LT: + if ((ptr += MINBPC(enc)) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, ASCII_EXCL)) { + if ((ptr += MINBPC(enc)) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, ASCII_LSQB)) { + ++level; + ptr += MINBPC(enc); + } + } + break; + case BT_RSQB: + if ((ptr += MINBPC(enc)) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) { + if ((ptr += MINBPC(enc)) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, ASCII_GT)) { + ptr += MINBPC(enc); + if (level == 0) { + *nextTokPtr = ptr; + return XML_TOK_IGNORE_SECT; + } + --level; + } + } + break; + default: + ptr += MINBPC(enc); + break; + } + } + return XML_TOK_PARTIAL; +} + +#endif /* XML_DTD */ + +static int PTRCALL +PREFIX(isPublicId)(const ENCODING *enc, const char *ptr, const char *end, + const char **badPtr) +{ + ptr += MINBPC(enc); + end -= MINBPC(enc); + for (; ptr != end; ptr += MINBPC(enc)) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_DIGIT: + case BT_HEX: + case BT_MINUS: + case BT_APOS: + case BT_LPAR: + case BT_RPAR: + case BT_PLUS: + case BT_COMMA: + case BT_SOL: + case BT_EQUALS: + case BT_QUEST: + case BT_CR: + case BT_LF: + case BT_SEMI: + case BT_EXCL: + case BT_AST: + case BT_PERCNT: + case BT_NUM: +#ifdef XML_NS + case BT_COLON: +#endif + break; + case BT_S: + if (CHAR_MATCHES(enc, ptr, ASCII_TAB)) { + *badPtr = ptr; + return 0; + } + break; + case BT_NAME: + case BT_NMSTRT: + if (!(BYTE_TO_ASCII(enc, ptr) & ~0x7f)) + break; + default: + switch (BYTE_TO_ASCII(enc, ptr)) { + case 0x24: /* $ */ + case 0x40: /* @ */ + break; + default: + *badPtr = ptr; + return 0; + } + break; + } + } + return 1; +} + +/* This must only be called for a well-formed start-tag or empty + element tag. Returns the number of attributes. Pointers to the + first attsMax attributes are stored in atts. +*/ + +static int PTRCALL +PREFIX(getAtts)(const ENCODING *enc, const char *ptr, + int attsMax, ATTRIBUTE *atts) +{ + enum { other, inName, inValue } state = inName; + int nAtts = 0; + int open = 0; /* defined when state == inValue; + initialization just to shut up compilers */ + + for (ptr += MINBPC(enc);; ptr += MINBPC(enc)) { + switch (BYTE_TYPE(enc, ptr)) { +#define START_NAME \ + if (state == other) { \ + if (nAtts < attsMax) { \ + atts[nAtts].name = ptr; \ + atts[nAtts].normalized = 1; \ + } \ + state = inName; \ + } +#define LEAD_CASE(n) \ + case BT_LEAD ## n: START_NAME ptr += (n - MINBPC(enc)); break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NONASCII: + case BT_NMSTRT: + case BT_HEX: + START_NAME + break; +#undef START_NAME + case BT_QUOT: + if (state != inValue) { + if (nAtts < attsMax) + atts[nAtts].valuePtr = ptr + MINBPC(enc); + state = inValue; + open = BT_QUOT; + } + else if (open == BT_QUOT) { + state = other; + if (nAtts < attsMax) + atts[nAtts].valueEnd = ptr; + nAtts++; + } + break; + case BT_APOS: + if (state != inValue) { + if (nAtts < attsMax) + atts[nAtts].valuePtr = ptr + MINBPC(enc); + state = inValue; + open = BT_APOS; + } + else if (open == BT_APOS) { + state = other; + if (nAtts < attsMax) + atts[nAtts].valueEnd = ptr; + nAtts++; + } + break; + case BT_AMP: + if (nAtts < attsMax) + atts[nAtts].normalized = 0; + break; + case BT_S: + if (state == inName) + state = other; + else if (state == inValue + && nAtts < attsMax + && atts[nAtts].normalized + && (ptr == atts[nAtts].valuePtr + || BYTE_TO_ASCII(enc, ptr) != ASCII_SPACE + || BYTE_TO_ASCII(enc, ptr + MINBPC(enc)) == ASCII_SPACE + || BYTE_TYPE(enc, ptr + MINBPC(enc)) == open)) + atts[nAtts].normalized = 0; + break; + case BT_CR: case BT_LF: + /* This case ensures that the first attribute name is counted + Apart from that we could just change state on the quote. */ + if (state == inName) + state = other; + else if (state == inValue && nAtts < attsMax) + atts[nAtts].normalized = 0; + break; + case BT_GT: + case BT_SOL: + if (state != inValue) + return nAtts; + break; + default: + break; + } + } + /* not reached */ +} + +static int PTRFASTCALL +PREFIX(charRefNumber)(const ENCODING *enc, const char *ptr) +{ + int result = 0; + /* skip &# */ + ptr += 2*MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_x)) { + for (ptr += MINBPC(enc); + !CHAR_MATCHES(enc, ptr, ASCII_SEMI); + ptr += MINBPC(enc)) { + int c = BYTE_TO_ASCII(enc, ptr); + switch (c) { + case ASCII_0: case ASCII_1: case ASCII_2: case ASCII_3: case ASCII_4: + case ASCII_5: case ASCII_6: case ASCII_7: case ASCII_8: case ASCII_9: + result <<= 4; + result |= (c - ASCII_0); + break; + case ASCII_A: case ASCII_B: case ASCII_C: + case ASCII_D: case ASCII_E: case ASCII_F: + result <<= 4; + result += 10 + (c - ASCII_A); + break; + case ASCII_a: case ASCII_b: case ASCII_c: + case ASCII_d: case ASCII_e: case ASCII_f: + result <<= 4; + result += 10 + (c - ASCII_a); + break; + } + if (result >= 0x110000) + return -1; + } + } + else { + for (; !CHAR_MATCHES(enc, ptr, ASCII_SEMI); ptr += MINBPC(enc)) { + int c = BYTE_TO_ASCII(enc, ptr); + result *= 10; + result += (c - ASCII_0); + if (result >= 0x110000) + return -1; + } + } + return checkCharRefNumber(result); +} + +static int PTRCALL +PREFIX(predefinedEntityName)(const ENCODING *enc, const char *ptr, + const char *end) +{ + switch ((end - ptr)/MINBPC(enc)) { + case 2: + if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_t)) { + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_l: + return ASCII_LT; + case ASCII_g: + return ASCII_GT; + } + } + break; + case 3: + if (CHAR_MATCHES(enc, ptr, ASCII_a)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_m)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_p)) + return ASCII_AMP; + } + } + break; + case 4: + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_q: + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_u)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_o)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_t)) + return ASCII_QUOT; + } + } + break; + case ASCII_a: + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_p)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_o)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_s)) + return ASCII_APOS; + } + } + break; + } + } + return 0; +} + +static int PTRCALL +PREFIX(sameName)(const ENCODING *enc, const char *ptr1, const char *ptr2) +{ + for (;;) { + switch (BYTE_TYPE(enc, ptr1)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (*ptr1++ != *ptr2++) \ + return 0; + LEAD_CASE(4) LEAD_CASE(3) LEAD_CASE(2) +#undef LEAD_CASE + /* fall through */ + if (*ptr1++ != *ptr2++) + return 0; + break; + case BT_NONASCII: + case BT_NMSTRT: +#ifdef XML_NS + case BT_COLON: +#endif + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + if (*ptr2++ != *ptr1++) + return 0; + if (MINBPC(enc) > 1) { + if (*ptr2++ != *ptr1++) + return 0; + if (MINBPC(enc) > 2) { + if (*ptr2++ != *ptr1++) + return 0; + if (MINBPC(enc) > 3) { + if (*ptr2++ != *ptr1++) + return 0; + } + } + } + break; + default: + if (MINBPC(enc) == 1 && *ptr1 == *ptr2) + return 1; + switch (BYTE_TYPE(enc, ptr2)) { + case BT_LEAD2: + case BT_LEAD3: + case BT_LEAD4: + case BT_NONASCII: + case BT_NMSTRT: +#ifdef XML_NS + case BT_COLON: +#endif + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + return 0; + default: + return 1; + } + } + } + /* not reached */ +} + +static int PTRCALL +PREFIX(nameMatchesAscii)(const ENCODING *enc, const char *ptr1, + const char *end1, const char *ptr2) +{ + for (; *ptr2; ptr1 += MINBPC(enc), ptr2++) { + if (ptr1 == end1) + return 0; + if (!CHAR_MATCHES(enc, ptr1, *ptr2)) + return 0; + } + return ptr1 == end1; +} + +static int PTRFASTCALL +PREFIX(nameLength)(const ENCODING *enc, const char *ptr) +{ + const char *start = ptr; + for (;;) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: ptr += n; break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NONASCII: + case BT_NMSTRT: +#ifdef XML_NS + case BT_COLON: +#endif + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + ptr += MINBPC(enc); + break; + default: + return (int)(ptr - start); + } + } +} + +static const char * PTRFASTCALL +PREFIX(skipS)(const ENCODING *enc, const char *ptr) +{ + for (;;) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_LF: + case BT_CR: + case BT_S: + ptr += MINBPC(enc); + break; + default: + return ptr; + } + } +} + +static void PTRCALL +PREFIX(updatePosition)(const ENCODING *enc, + const char *ptr, + const char *end, + POSITION *pos) +{ + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + ptr += n; \ + break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_LF: + pos->columnNumber = (XML_Size)-1; + pos->lineNumber++; + ptr += MINBPC(enc); + break; + case BT_CR: + pos->lineNumber++; + ptr += MINBPC(enc); + if (ptr != end && BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + pos->columnNumber = (XML_Size)-1; + break; + default: + ptr += MINBPC(enc); + break; + } + pos->columnNumber++; + } +} + +#undef DO_LEAD_CASE +#undef MULTIBYTE_CASES +#undef INVALID_CASES +#undef CHECK_NAME_CASE +#undef CHECK_NAME_CASES +#undef CHECK_NMSTRT_CASE +#undef CHECK_NMSTRT_CASES + diff --git a/src/lib/expat/xmltok_impl.h b/src/lib/expat/xmltok_impl.h new file mode 100644 index 00000000000..da0ea60a657 --- /dev/null +++ b/src/lib/expat/xmltok_impl.h @@ -0,0 +1,46 @@ +/* +Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd +See the file COPYING for copying permission. +*/ + +enum { + BT_NONXML, + BT_MALFORM, + BT_LT, + BT_AMP, + BT_RSQB, + BT_LEAD2, + BT_LEAD3, + BT_LEAD4, + BT_TRAIL, + BT_CR, + BT_LF, + BT_GT, + BT_QUOT, + BT_APOS, + BT_EQUALS, + BT_QUEST, + BT_EXCL, + BT_SOL, + BT_SEMI, + BT_NUM, + BT_LSQB, + BT_S, + BT_NMSTRT, + BT_COLON, + BT_HEX, + BT_DIGIT, + BT_NAME, + BT_MINUS, + BT_OTHER, /* known not to be a name or name start character */ + BT_NONASCII, /* might be a name or name start character */ + BT_PERCNT, + BT_LPAR, + BT_RPAR, + BT_AST, + BT_PLUS, + BT_COMMA, + BT_VERBAR +}; + +#include <stddef.h> diff --git a/src/lib/expat/xmltok_ns.c b/src/lib/expat/xmltok_ns.c new file mode 100644 index 00000000000..d2f893836fb --- /dev/null +++ b/src/lib/expat/xmltok_ns.c @@ -0,0 +1,106 @@ +const ENCODING * +NS(XmlGetUtf8InternalEncoding)(void) +{ + return &ns(internal_utf8_encoding).enc; +} + +const ENCODING * +NS(XmlGetUtf16InternalEncoding)(void) +{ +#if BYTEORDER == 1234 + return &ns(internal_little2_encoding).enc; +#elif BYTEORDER == 4321 + return &ns(internal_big2_encoding).enc; +#else + const short n = 1; + return (*(const char *)&n + ? &ns(internal_little2_encoding).enc + : &ns(internal_big2_encoding).enc); +#endif +} + +static const ENCODING * const NS(encodings)[] = { + &ns(latin1_encoding).enc, + &ns(ascii_encoding).enc, + &ns(utf8_encoding).enc, + &ns(big2_encoding).enc, + &ns(big2_encoding).enc, + &ns(little2_encoding).enc, + &ns(utf8_encoding).enc /* NO_ENC */ +}; + +static int PTRCALL +NS(initScanProlog)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + return initScan(NS(encodings), (const INIT_ENCODING *)enc, + XML_PROLOG_STATE, ptr, end, nextTokPtr); +} + +static int PTRCALL +NS(initScanContent)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + return initScan(NS(encodings), (const INIT_ENCODING *)enc, + XML_CONTENT_STATE, ptr, end, nextTokPtr); +} + +int +NS(XmlInitEncoding)(INIT_ENCODING *p, const ENCODING **encPtr, + const char *name) +{ + int i = getEncodingIndex(name); + if (i == UNKNOWN_ENC) + return 0; + SET_INIT_ENC_INDEX(p, i); + p->initEnc.scanners[XML_PROLOG_STATE] = NS(initScanProlog); + p->initEnc.scanners[XML_CONTENT_STATE] = NS(initScanContent); + p->initEnc.updatePosition = initUpdatePosition; + p->encPtr = encPtr; + *encPtr = &(p->initEnc); + return 1; +} + +static const ENCODING * +NS(findEncoding)(const ENCODING *enc, const char *ptr, const char *end) +{ +#define ENCODING_MAX 128 + char buf[ENCODING_MAX]; + char *p = buf; + int i; + XmlUtf8Convert(enc, &ptr, end, &p, p + ENCODING_MAX - 1); + if (ptr != end) + return 0; + *p = 0; + if (streqci(buf, KW_UTF_16) && enc->minBytesPerChar == 2) + return enc; + i = getEncodingIndex(buf); + if (i == UNKNOWN_ENC) + return 0; + return NS(encodings)[i]; +} + +int +NS(XmlParseXmlDecl)(int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **versionEndPtr, + const char **encodingName, + const ENCODING **encoding, + int *standalone) +{ + return doParseXmlDecl(NS(findEncoding), + isGeneralTextEntity, + enc, + ptr, + end, + badPtr, + versionPtr, + versionEndPtr, + encodingName, + encoding, + standalone); +} diff --git a/src/lib/lib.mak b/src/lib/lib.mak new file mode 100644 index 00000000000..224caa63cf3 --- /dev/null +++ b/src/lib/lib.mak @@ -0,0 +1,84 @@ +########################################################################### +# +# lib.mak +# +# MAME dependent library makefile +# +# Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +# Visit http://mamedev.org for licensing and usage restrictions. +# +########################################################################### + + +LIBOBJ = $(OBJ)/lib + +OBJDIRS += \ + $(LIBOBJ)/util \ + $(LIBOBJ)/expat \ + $(LIBOBJ)/zlib \ + + + +#------------------------------------------------- +# utility library objects +#------------------------------------------------- + +UTILOBJS = \ + $(LIBOBJ)/util/astring.o \ + $(LIBOBJ)/util/avcomp.o \ + $(LIBOBJ)/util/aviio.o \ + $(LIBOBJ)/util/bitmap.o \ + $(LIBOBJ)/util/cdrom.o \ + $(LIBOBJ)/util/chd.o \ + $(LIBOBJ)/util/corefile.o \ + $(LIBOBJ)/util/corestr.o \ + $(LIBOBJ)/util/coreutil.o \ + $(LIBOBJ)/util/harddisk.o \ + $(LIBOBJ)/util/huffman.o \ + $(LIBOBJ)/util/jedparse.o \ + $(LIBOBJ)/util/md5.o \ + $(LIBOBJ)/util/options.o \ + $(LIBOBJ)/util/palette.o \ + $(LIBOBJ)/util/png.o \ + $(LIBOBJ)/util/pool.o \ + $(LIBOBJ)/util/sha1.o \ + $(LIBOBJ)/util/unicode.o \ + $(LIBOBJ)/util/unzip.o \ + $(LIBOBJ)/util/xmlfile.o \ + +$(OBJ)/libutil.a: $(UTILOBJS) + + + +#------------------------------------------------- +# expat library objects +#------------------------------------------------- + +EXPATOBJS = \ + $(LIBOBJ)/expat/xmlparse.o \ + $(LIBOBJ)/expat/xmlrole.o \ + $(LIBOBJ)/expat/xmltok.o + +$(OBJ)/libexpat.a: $(EXPATOBJS) + + + +#------------------------------------------------- +# zlib library objects +#------------------------------------------------- + +ZLIBOBJS = \ + $(LIBOBJ)/zlib/adler32.o \ + $(LIBOBJ)/zlib/compress.o \ + $(LIBOBJ)/zlib/crc32.o \ + $(LIBOBJ)/zlib/deflate.o \ + $(LIBOBJ)/zlib/gzio.o \ + $(LIBOBJ)/zlib/inffast.o \ + $(LIBOBJ)/zlib/inflate.o \ + $(LIBOBJ)/zlib/infback.o \ + $(LIBOBJ)/zlib/inftrees.o \ + $(LIBOBJ)/zlib/trees.o \ + $(LIBOBJ)/zlib/uncompr.o \ + $(LIBOBJ)/zlib/zutil.o + +$(OBJ)/libz.a: $(ZLIBOBJS) diff --git a/src/lib/util/astring.c b/src/lib/util/astring.c new file mode 100644 index 00000000000..b2ae0c6c61d --- /dev/null +++ b/src/lib/util/astring.c @@ -0,0 +1,596 @@ +/*************************************************************************** + + astring.c + + Allocated string manipulation functions. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +****************************************************************************/ + +#include "astring.h" +#include "osdcore.h" + +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> +#include <ctype.h> + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +struct _astring +{ + char * text; + int alloclen; + char smallbuf[256 - sizeof(int) - sizeof(char *)]; +}; + + + +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +static char dummy_text[2] = { 0 }; +static astring dummy_astring = { dummy_text, 1, { 0 } }; + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + ensure_room - ensure we have room for a + given string, or else reallocate our buffer +-------------------------------------------------*/ + +INLINE int ensure_room(astring *str, int length) +{ + char *newbuf, *oldbuf; + int alloclen; + + /* always fail to expand the dummy */ + if (str == &dummy_astring) + return FALSE; + + /* if we have the room, do nothing */ + if (str->alloclen >= length + 1) + return TRUE; + + /* allocate a new buffer with some slop */ + alloclen = length + 256; + newbuf = malloc(alloclen); + if (newbuf == NULL) + return FALSE; + + /* swap in the new buffer and free the old one */ + oldbuf = (str->text == str->smallbuf) ? NULL : str->text; + str->text = strcpy(newbuf, str->text); + str->alloclen = alloclen; + if (oldbuf != NULL) + free(oldbuf); + + return TRUE; +} + + +/*------------------------------------------------- + safe_string_base - return a "safe" string + base for a given start index +-------------------------------------------------*/ + +INLINE char *safe_string_base(char *base, int start) +{ + int max = strlen(base); + return (start >= 0 && start < max) ? base + start : base + max; +} + + +/*------------------------------------------------- + normalize_substr - normalize substr parameters +-------------------------------------------------*/ + +INLINE void normalize_substr(int *start, int *count, int length) +{ + /* limit start */ + if (*start < 0) + *start = 0; + else if (*start > length) + *start = length; + + /* update count */ + if (*count == -1 || *start + *count > length) + *count = length - *start; +} + + + +/*************************************************************************** + ASTRING ALLOCATION +***************************************************************************/ + +/*------------------------------------------------- + astring_alloc - allocate a new astring +-------------------------------------------------*/ + +astring *astring_alloc(void) +{ + astring *str; + + /* allocate memory; if we fail, return the dummy */ + str = malloc(sizeof(*str)); + if (str == NULL) + return &dummy_astring; + memset(str, 0, sizeof(*str)); + + /* initialize the small buffer */ + str->text = str->smallbuf; + str->alloclen = ARRAY_LENGTH(str->smallbuf); + return str; +} + + +/*------------------------------------------------- + astring_free - free an astring +-------------------------------------------------*/ + +void astring_free(astring *str) +{ + /* ignore attempts to free the dummy */ + if (str == &dummy_astring) + return; + + /* if we allocated additional memory, free that */ + if (str->text != str->smallbuf) + free(str->text); + free(str); +} + + + +/*************************************************************************** + INLINE ASTRING CHANGES +***************************************************************************/ + +/*------------------------------------------------- + astring_cpy - copy one astring into another +-------------------------------------------------*/ + +astring *astring_cpy(astring *dst, const astring *src) +{ + return astring_cpyc(dst, src->text); +} + + +/*------------------------------------------------- + astring_cpyc - copy a C string into an astring +-------------------------------------------------*/ + +astring *astring_cpyc(astring *dst, const char *src) +{ + return astring_cpych(dst, src, strlen(src)); +} + + +/*------------------------------------------------- + astring_cpych - copy a character array into + an astring +-------------------------------------------------*/ + +astring *astring_cpych(astring *dst, const char *src, int count) +{ + /* make room; if we fail or if dst is the dummy, do nothing */ + if (!ensure_room(dst, count)) + return dst; + + /* copy the raw data and NULL-terminate */ + if (count > 0) + memcpy(dst->text, src, count); + dst->text[count] = 0; + return dst; +} + + +/*------------------------------------------------- + astring_cpysubstr - copy a substring of one + string to another +-------------------------------------------------*/ + +astring *astring_cpysubstr(astring *dst, const astring *src, int start, int count) +{ + normalize_substr(&start, &count, strlen(src->text)); + return astring_cpych(dst, src->text + start, count); +} + + +/*------------------------------------------------- + astring_ins - insert one astring into another +-------------------------------------------------*/ + +astring *astring_ins(astring *dst, int insbefore, const astring *src) +{ + return astring_insc(dst, insbefore, src->text); +} + + +/*------------------------------------------------- + astring_insc - insert a C string into an + astring +-------------------------------------------------*/ + +astring *astring_insc(astring *dst, int insbefore, const char *src) +{ + return astring_insch(dst, insbefore, src, strlen(src)); +} + + +/*------------------------------------------------- + astring_insch - insert a character array + into an astring +-------------------------------------------------*/ + +astring *astring_insch(astring *dst, int insbefore, const char *src, int count) +{ + int dstlength = strlen(dst->text); + + /* make room; if we fail or if dst is the dummy, do nothing */ + if (!ensure_room(dst, dstlength + count)) + return dst; + + /* adjust insbefore to be logical */ + if (insbefore < 0 || insbefore > dstlength) + insbefore = dstlength; + + /* copy the data an NULL-terminate */ + if (insbefore < dstlength) + memmove(dst->text + insbefore + count, dst->text + insbefore, dstlength - insbefore); + memcpy(dst->text + insbefore, src, count); + dst->text[dstlength + count] = 0; + return dst; +} + + +/*------------------------------------------------- + astring_inssubstr - insert a substring of + one string to another +-------------------------------------------------*/ + +astring *astring_inssubstr(astring *dst, int insbefore, const astring *src, int start, int count) +{ + normalize_substr(&start, &count, strlen(src->text)); + return astring_insch(dst, insbefore, src->text + start, count); +} + + +/*------------------------------------------------- + astring_substr - extract a substring of + ourself, removing everything else +-------------------------------------------------*/ + +astring *astring_substr(astring *str, int start, int count) +{ + /* ignore attempts to do this on the dummy */ + if (str == &dummy_astring) + return str; + + /* normalize parameters */ + normalize_substr(&start, &count, strlen(str->text)); + + /* move the data and NULL-terminate */ + if (count > 0 && start > 0) + memmove(str->text, str->text + start, count); + str->text[count] = 0; + return str; +} + + +/*------------------------------------------------- + astring_printf - printf text into an astring +-------------------------------------------------*/ + +int astring_printf(astring *dst, const char *format, ...) +{ + char tempbuf[4096]; + va_list args; + int result; + + /* sprintf into the temporary buffer */ + va_start(args, format); + result = vsprintf(tempbuf, format, args); + va_end(args); + + /* set the result */ + astring_cpyc(dst, tempbuf); + return result; +} + + + +/*************************************************************************** + ASTRING QUERIES +***************************************************************************/ + +/*------------------------------------------------- + astring_c - return a pointer to a C string + from an astring +-------------------------------------------------*/ + +const char *astring_c(const astring *str) +{ + return str->text; +} + + +/*------------------------------------------------- + astring_len - return the length of an astring +-------------------------------------------------*/ + +int astring_len(const astring *str) +{ + return strlen(str->text); +} + + +/*------------------------------------------------- + astring_cmp - compare one astring to another +-------------------------------------------------*/ + +int astring_cmp(const astring *str1, const astring *str2) +{ + return astring_cmpc(str1, str2->text); +} + + +/*------------------------------------------------- + astring_cmpc - compare a C string to an astring +-------------------------------------------------*/ + +int astring_cmpc(const astring *str1, const char *str2) +{ + const char *s1 = str1->text; + + /* loop while equal until we hit the end of strings */ + while (*s1 != 0 && *str2 != 0 && *s1 == *str2) + s1++, str2++; + return *s1 - *str2; +} + + +/*------------------------------------------------- + astring_cmpch - compare a character array to + an astring +-------------------------------------------------*/ + +int astring_cmpch(const astring *str1, const char *str2, int count) +{ + const char *s1 = str1->text; + int result; + + /* loop while equal until we hit the end of strings */ + while (count-- > 0 && *s1 != 0 && *str2 != 0 && *s1 == *str2) + s1++, str2++; + result = (count == -1) ? 0 : *s1 - *str2; + if (result == 0 && *s1 != 0) + result = 1; + return result; +} + + +/*------------------------------------------------- + astring_cmpsubstr - compare a substring to + an astring +-------------------------------------------------*/ + +int astring_cmpsubstr(const astring *str1, const astring *str2, int start, int count) +{ + normalize_substr(&start, &count, strlen(str2->text)); + return astring_cmpch(str1, str2->text + start, count); +} + + +/*------------------------------------------------- + astring_icmp - case-insenstive compare one + astring to another +-------------------------------------------------*/ + +int astring_icmp(const astring *str1, const astring *str2) +{ + return astring_icmpc(str1, str2->text); +} + + +/*------------------------------------------------- + astring_icmpc - case-insenstive compare a C + string to an astring +-------------------------------------------------*/ + +int astring_icmpc(const astring *str1, const char *str2) +{ + const char *s1 = str1->text; + + /* loop while equal until we hit the end of strings */ + while (*s1 != 0 && *str2 != 0 && tolower(*s1) == tolower(*str2)) + s1++, str2++; + return tolower(*s1) - tolower(*str2); +} + + +/*------------------------------------------------- + astring_icmpch - case-insenstive compare a + character array to an astring +-------------------------------------------------*/ + +int astring_icmpch(const astring *str1, const char *str2, int count) +{ + const char *s1 = str1->text; + int result; + + /* loop while equal until we hit the end of strings */ + while (count-- > 0 && *s1 != 0 && *str2 != 0 && tolower(*s1) == tolower(*str2)) + s1++, str2++; + result = (count == -1) ? 0 : tolower(*s1) - tolower(*str2); + if (result == 0 && *s1 != 0) + result = 1; + return result; +} + + +/*------------------------------------------------- + astring_icmpsubstr - case-insenstive compare a + substring to an astring +-------------------------------------------------*/ + +int astring_icmpsubstr(const astring *str1, const astring *str2, int start, int count) +{ + normalize_substr(&start, &count, strlen(str2->text)); + return astring_icmpch(str1, str2->text + start, count); +} + + +/*------------------------------------------------- + astring_chr - return the index of a character + in an astring +-------------------------------------------------*/ + +int astring_chr(const astring *str, int start, int ch) +{ + char *result = strchr(safe_string_base(str->text, start), ch); + return (result != NULL) ? (result - str->text) : -1; +} + + +/*------------------------------------------------- + astring_rchr - return the index of a character + in an astring, searching from the end +-------------------------------------------------*/ + +int astring_rchr(const astring *str, int start, int ch) +{ + char *result = strrchr(safe_string_base(str->text, start), ch); + return (result != NULL) ? (result - str->text) : -1; +} + + +/*------------------------------------------------- + astring_find - find one astring in another +-------------------------------------------------*/ + +int astring_find(const astring *str, int start, const astring *search) +{ + return astring_findc(str, start, search->text); +} + + +/*------------------------------------------------- + astring_findc - find a C string in an astring +-------------------------------------------------*/ + +int astring_findc(const astring *str, int start, const char *search) +{ + char *result = strstr(safe_string_base(str->text, start), search); + return (result != NULL) ? (result - str->text) : -1; +} + + + +/*************************************************************************** + ASTRING UTILITIES +***************************************************************************/ + +/*------------------------------------------------- + astring_delchr - delete all instances of + 'ch' +-------------------------------------------------*/ + +astring *astring_delchr(astring *str, int ch) +{ + char *src, *dst; + + /* simple deletion */ + for (src = dst = str->text; *src != 0; src++) + if (*src != ch) + *dst++ = *src; + *dst = 0; + + return str; +} + + +/*------------------------------------------------- + astring_replacechr - replace all instances of + 'ch' with 'newch' +-------------------------------------------------*/ + +astring *astring_replacechr(astring *str, int ch, int newch) +{ + char *text; + + /* simple replacement */ + for (text = str->text; *text != 0; text++) + if (*text == ch) + *text = newch; + + return str; +} + + +/*------------------------------------------------- + astring_toupper - convert string to all + upper-case +-------------------------------------------------*/ + +astring *astring_toupper(astring *str) +{ + char *text; + + /* just toupper() on all characters */ + for (text = str->text; *text != 0; text++) + *text = toupper(*text); + + return str; +} + + +/*------------------------------------------------- + astring_tolower - convert string to all + lower-case +-------------------------------------------------*/ + +astring *astring_tolower(astring *str) +{ + char *text; + + /* just toupper() on all characters */ + for (text = str->text; *text != 0; text++) + *text = tolower(*text); + + return str; +} + + +/*------------------------------------------------- + astring_trimspace - remove all space + characters from beginning/end +-------------------------------------------------*/ + +astring *astring_trimspace(astring *str) +{ + char *ptr; + + /* first remove stuff from the end */ + for (ptr = str->text + strlen(str->text) - 1; ptr >= str->text && isspace(*ptr); ptr--) + *ptr = 0; + + /* then count how much to remove from the beginning */ + for (ptr = str->text; *ptr != 0 && isspace(*ptr); ptr++) ; + if (ptr > str->text) + astring_substr(str, ptr - str->text, -1); + + return str; +} diff --git a/src/lib/util/astring.h b/src/lib/util/astring.h new file mode 100644 index 00000000000..3d80216383c --- /dev/null +++ b/src/lib/util/astring.h @@ -0,0 +1,228 @@ +/*************************************************************************** + + astring.h + + Allocated string manipulation functions. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#pragma once + +#ifndef __ASTRING_H__ +#define __ASTRING_H__ + +#include "pool.h" + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +typedef struct _astring astring; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + + +/* ----- astring allocation ----- */ + +/* allocate a new astring */ +astring *astring_alloc(void); + +/* free an astring */ +void astring_free(astring *str); + + + +/* ----- inline astring changes ----- */ + +/* copy one astring into another */ +astring *astring_cpy(astring *dst, const astring *src); + +/* copy a C string into an astring */ +astring *astring_cpyc(astring *dst, const char *src); + +/* copy a character array into an astring */ +astring *astring_cpych(astring *dst, const char *src, int count); + +/* copy a substring of one string into another */ +astring *astring_cpysubstr(astring *dst, const astring *src, int start, int count); + +/* insert one astring into another */ +astring *astring_ins(astring *dst, int insbefore, const astring *src); + +/* insert a C string into an astring */ +astring *astring_insc(astring *dst, int insbefore, const char *src); + +/* insert a character array into an astring */ +astring *astring_insch(astring *dst, int insbefore, const char *src, int count); + +/* insert a substring of one string into another */ +astring *astring_inssubstr(astring *dst, int insbefore, const astring *src, int start, int count); + +/* extract a substring of ourself, removing everything else */ +astring *astring_substr(astring *str, int start, int count); + +/* formatted printf to an astring */ +int astring_printf(astring *dst, const char *format, ...); + + + +/* ----- astring queries ----- */ + +/* return a pointer to a C string from an astring */ +const char *astring_c(const astring *str); + +/* return the length of an astring */ +int astring_len(const astring *str); + +/* compare two astrings */ +int astring_cmp(const astring *str1, const astring *str2); + +/* compare an astring to a C string */ +int astring_cmpc(const astring *str1, const char *str2); + +/* compare an astring to a character buffer */ +int astring_cmpch(const astring *str1, const char *str2, int count); + +/* compare an astring to a character buffer */ +int astring_cmpsubstr(const astring *str1, const astring *str2, int start, int count); + +/* case-insenstive compare two astrings */ +int astring_icmp(const astring *str1, const astring *str2); + +/* case-insenstive compare an astring to a C string */ +int astring_icmpc(const astring *str1, const char *str2); + +/* case-insenstive compare an astring to a character buffer */ +int astring_icmpch(const astring *str1, const char *str2, int count); + +/* case-insenstive compare an astring to a character buffer */ +int astring_icmpsubstr(const astring *str1, const astring *str2, int start, int count); + +/* search an astring for a character, returning offset or -1 if not found */ +int astring_chr(const astring *str, int start, int ch); + +/* reverse search an astring for a character, returning offset or -1 if not found */ +int astring_rchr(const astring *str, int start, int ch); + +/* search in an astring for another astring, returning offset or -1 if not found */ +int astring_find(const astring *str, int start, const astring *search); + +/* search in an astring for a C string, returning offset or -1 if not found */ +int astring_findc(const astring *str, int start, const char *search); + + + +/* ----- astring utilties ----- */ + +/* delete all instances of 'ch' */ +astring *astring_delchr(astring *str, int ch); + +/* replace all instances of 'ch' with 'newch' */ +astring *astring_replacechr(astring *str, int ch, int newch); + +/* convert string to all upper-case */ +astring *astring_toupper(astring *str); + +/* convert string to all lower-case */ +astring *astring_tolower(astring *str); + +/* remove all space characters from beginning/end */ +astring *astring_trimspace(astring *str); + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/* allocate a new duplicate of an astring */ +INLINE astring *astring_dup(const astring *str) +{ + return astring_cpy(astring_alloc(), str); +} + +/* allocate a new duplicate of an astring */ +INLINE astring *astring_dupc(const char *str) +{ + return astring_cpyc(astring_alloc(), str); +} + +/* allocate a new duplicate of an astring */ +INLINE astring *astring_dupch(const char *str, int count) +{ + return astring_cpych(astring_alloc(), str, count); +} + +/* allocate a duplicate of a substring */ +INLINE astring *astring_dupsubstr(const astring *str, int start, int count) +{ + return astring_cpysubstr(astring_alloc(), str, start, count); +} + + +/* reset an astring to an empty string */ +INLINE astring *astring_reset(astring *dst) +{ + return astring_cpyc(dst, ""); +} + + +/* concatenate one astring to the end of another */ +INLINE astring *astring_cat(astring *dst, const astring *src) +{ + return astring_ins(dst, -1, src); +} + +/* concatenate a C string to the end of an astring */ +INLINE astring *astring_catc(astring *dst, const char *src) +{ + return astring_insc(dst, -1, src); +} + +/* concatenate a character array to the end of an astring */ +INLINE astring *astring_catch(astring *dst, const char *src, int count) +{ + return astring_insch(dst, -1, src, count); +} + +/* concatenate a substring of one string into another */ +INLINE astring *astring_catsubstr(astring *dst, const astring *src, int start, int count) +{ + return astring_inssubstr(dst, -1, src, start, count); +} + + +/* assemble an astring from 2 C strings */ +INLINE astring *astring_assemble_2(astring *dst, const char *src1, const char *src2) +{ + return astring_catc(astring_cpyc(dst, src1), src2); +} + +/* assemble an astring from 3 C strings */ +INLINE astring *astring_assemble_3(astring *dst, const char *src1, const char *src2, const char *src3) +{ + return astring_catc(astring_assemble_2(dst, src1, src2), src3); +} + +/* assemble an astring from 4 C strings */ +INLINE astring *astring_assemble_4(astring *dst, const char *src1, const char *src2, const char *src3, const char *src4) +{ + return astring_catc(astring_assemble_3(dst, src1, src2, src3), src4); +} + +/* assemble an astring from 5 C strings */ +INLINE astring *astring_assemble_5(astring *dst, const char *src1, const char *src2, const char *src3, const char *src4, const char *src5) +{ + return astring_catc(astring_assemble_4(dst, src1, src2, src3, src4), src5); +} + + +#endif /* __ASTRING_H__ */ diff --git a/src/lib/util/avcomp.c b/src/lib/util/avcomp.c new file mode 100644 index 00000000000..d3144e01175 --- /dev/null +++ b/src/lib/util/avcomp.c @@ -0,0 +1,840 @@ +/* +To do: +* verify Cb,Y,Cr,Y ordering +* swap the ordering(?) +* add backchannel support for samples as well? +*/ +/*************************************************************************** + + avcomp.c + + Audio/video compression and decompression helpers. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**************************************************************************** + + Each frame is compressed as a unit. The raw data is of the form: + (all multibyte values are stored in big-endian format) + + 'chav' (4 bytes) - fixed header data to identify the format + metasize (1 byte) - size of metadata in bytes (max=255 bytes) + channels (1 byte) - number of audio channels + samples (2 bytes) - number of samples per audio stream + width (2 bytes) - width of video data + height (2 bytes) - height of video data (high bit set means interlaced) + <metadata> - as raw bytes + <audio stream 0> - as signed 16-bit samples + <audio stream 1> - as signed 16-bit samples + ... + <video data> - as a raw array of 8-bit YUY data in (Cb,Y,Cr,Y) order + + When compressed, the data is stored as follows: + (all multibyte values are stored in big-endian format) + + metasize (1 byte) - size of metadata in bytes + channels (1 byte) - number of audio channels + samples (2 bytes) - number of samples per audio stream + width (2 bytes) - width of video data + height (2 bytes) - height of video data (high bit set means interlaced) + audhuffsize (1 byte) - size of the audio Huffman table + str0size (2 bytes) - compressed size of stream 0 + str1size (2 bytes) - compressed size of stream 1 + ... + <metadata> - as raw data + <audio huffman table> - Huffman table for audio decoding + <audio stream 0 data> - Huffman-compressed deltas + <audio stream 1 data> - Huffman-compressed deltas + <...> + <video huffman table> - Huffman table for video decoding + <video data> - compressed data + +**************************************************************************** + + Attempted techniques that have not been worthwhile: + + * Attempted to use integer DCTs from the IJG code; even the "slow" + variants produce a lot of error and thus kill our compression ratio, + since our compression is based on error not bitrate. + + * Tried various other predictors for the lossless video encoding, but + none tended to give any significant gain over predicting the + previous pixel. + +***************************************************************************/ + +#include "avcomp.h" +#include "huffman.h" +#include "chd.h" + +#include <math.h> + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +struct _avcomp_state +{ + /* decompression parameters */ + UINT32 decodemask; + UINT8 * videobuffer; + UINT32 videostride; + UINT32 videoxor; + UINT32 audioxor; + + /* video parameters */ + UINT32 maxwidth, maxheight; + + /* audio parameters */ + UINT32 maxchannels; + + /* intermediate data */ + UINT8 * deltadata; + UINT8 * audiodata; + + /* huffman contexts */ + huffman_context * ycontext; + huffman_context * ccontext; + huffman_context * audiocontext; +}; + + + +/*************************************************************************** + PROTOTYPES +***************************************************************************/ + +/* encoding helpers */ +static avcomp_error encode_audio(avcomp_state *state, int channels, int samples, const UINT8 *source, UINT8 *dest, UINT8 *sizes); +static avcomp_error encode_video(avcomp_state *state, int width, int height, int interlaced, const UINT8 *source, UINT8 *dest, UINT32 *complength); +static avcomp_error encode_video_lossless(avcomp_state *state, int width, int height, int interlaced, const UINT8 *source, UINT8 *dest, UINT32 *complength); + +/* decoding helpers */ +static avcomp_error decode_audio(avcomp_state *state, int channels, int samples, const UINT8 *source, UINT8 *dest, const UINT8 *sizes, UINT32 destxor); +static avcomp_error decode_video(avcomp_state *state, int width, int height, int interlaced, const UINT8 *source, UINT32 complength, UINT8 *dest); +static avcomp_error decode_video_lossless(avcomp_state *state, int width, int height, int interlaced, const UINT8 *source, UINT32 complength, UINT8 *dest, UINT32 deststride, UINT32 destxor); + + + +/*************************************************************************** + IMPLEMENTATION +***************************************************************************/ + +/*------------------------------------------------- + avcomp_init - allocate and initialize a + new state block for compression or + decompression +-------------------------------------------------*/ + +avcomp_state *avcomp_init(UINT32 maxwidth, UINT32 maxheight, UINT32 maxchannels) +{ + huffman_error hufferr; + avcomp_state *state; + + /* allocate memory for state block */ + state = malloc(sizeof(*state)); + if (state == NULL) + return NULL; + + /* clear the buffers */ + memset(state, 0, sizeof(*state)); + + /* fill in sensible decompression defaults */ + state->decodemask = AVCOMP_DECODE_DEFAULT; + + /* compute the core info */ + state->maxwidth = maxwidth; + state->maxheight = maxheight; + state->maxchannels = maxchannels; + + /* now allocate data buffers */ + state->deltadata = malloc(state->maxwidth * state->maxheight * 2); + state->audiodata = malloc(65536 * state->maxchannels * 2); + if (state->deltadata == NULL || state->audiodata == NULL) + goto cleanup; + + /* create huffman contexts */ + hufferr = huffman_create_context(&state->ycontext, 12); + if (hufferr != HUFFERR_NONE) + goto cleanup; + hufferr = huffman_create_context(&state->ccontext, 12); + if (hufferr != HUFFERR_NONE) + goto cleanup; + hufferr = huffman_create_context(&state->audiocontext, 12); + if (hufferr != HUFFERR_NONE) + goto cleanup; + + return state; + +cleanup: + avcomp_free(state); + return NULL; +} + + +/*------------------------------------------------- + avcomp_free - free a state block +-------------------------------------------------*/ + +void avcomp_free(avcomp_state *state) +{ + /* free the data buffers */ + if (state->deltadata != NULL) + free(state->deltadata); + if (state->audiodata != NULL) + free(state->audiodata); + + /* free the contexts */ + if (state->ycontext != NULL) + huffman_free_context(state->ycontext); + if (state->ccontext != NULL) + huffman_free_context(state->ccontext); + if (state->audiocontext != NULL) + huffman_free_context(state->audiocontext); + + free(state); +} + + +/*------------------------------------------------- + avcomp_decompress_config - configure compression + parameters +-------------------------------------------------*/ + +void avcomp_decompress_config(avcomp_state *state, UINT32 decodemask, UINT8 *videobuffer, UINT32 videostride, UINT32 videoxor, UINT32 audioxor) +{ + state->decodemask = decodemask; + state->videobuffer = videobuffer; + state->videostride = videostride; + state->videoxor = videoxor; + state->audioxor = audioxor; +} + + + +/*************************************************************************** + ENCODING/DECODING FRONTENDS +***************************************************************************/ + +/*------------------------------------------------- + avcomp_encode_data - encode a block of data + into a compressed data stream +-------------------------------------------------*/ + +avcomp_error avcomp_encode_data(avcomp_state *state, const UINT8 *source, UINT8 *dest, UINT32 *complength) +{ + UINT32 metasize, channels, samples, width, height; + UINT32 srcoffs, dstoffs; + avcomp_error err; + int interlaced; + + /* validate the header */ + if (source[0] != 'c' || source[1] != 'h' || source[2] != 'a' || source[3] != 'v') + return AVCERR_INVALID_DATA; + + /* extract info from the header */ + metasize = source[4]; + channels = source[5]; + samples = (source[6] << 8) + source[7]; + width = (source[8] << 8) + source[9]; + height = (source[10] << 8) + source[11]; + interlaced = (height >> 15) & 1; + height &= 0x7fff; + + /* validate the info from the header */ + if (width > state->maxwidth || height > state->maxheight) + return AVCERR_VIDEO_TOO_LARGE; + if (channels > state->maxchannels) + return AVCERR_AUDIO_TOO_LARGE; + + /* write the basics to the new header */ + dest[0] = metasize; + dest[1] = channels; + dest[2] = samples >> 8; + dest[3] = samples; + dest[4] = width >> 8; + dest[5] = width; + dest[6] = (interlaced << 7) | (height >> 8); + dest[7] = height; + + /* starting offsets */ + srcoffs = 12; + dstoffs = 9 + 2 * channels; + + /* copy the metadata first */ + if (metasize > 0) + { + memcpy(dest + dstoffs, source + srcoffs, metasize); + srcoffs += metasize; + dstoffs += metasize; + } + + /* encode the audio channels */ + if (channels > 0) + { + int chnum; + + /* encode the audio */ + err = encode_audio(state, channels, samples, source + srcoffs, dest + dstoffs, &dest[8]); + if (err != AVCERR_NONE) + return err; + + /* advance the pointers past the data */ + srcoffs += channels * samples * 2; + dstoffs += dest[8]; + for (chnum = 0; chnum < channels; chnum++) + dstoffs += (dest[9 + 2 * chnum] << 8) + dest[10 + 2 * chnum]; + } + + /* encode the video data */ + if (width > 0 && height > 0) + { + UINT32 vidlength; + + /* encode the video */ + err = encode_video(state, width, height, interlaced, source + srcoffs, dest + dstoffs, &vidlength); + if (err != AVCERR_NONE) + return err; + + /* advance the pointers past the data */ + srcoffs += width * height * 2; + dstoffs += vidlength; + } + + /* set the total compression */ + *complength = dstoffs; + return AVCERR_NONE; +} + + +/*------------------------------------------------- + avcomp_decode_data - decode both + audio and video from a raw data stream +-------------------------------------------------*/ + +avcomp_error avcomp_decode_data(avcomp_state *state, const UINT8 *source, UINT32 complength, UINT8 *dest) +{ + UINT32 metasize, channels, samples, width, height; + UINT32 srcoffs, dstoffs, totalsize; + avcomp_error err; + int interlaced; + int chnum; + + /* extract info from the header */ + if (complength < 8) + return AVCERR_INVALID_DATA; + metasize = source[0]; + channels = source[1]; + samples = (source[2] << 8) + source[3]; + width = (source[4] << 8) + source[5]; + height = (source[6] << 8) + source[7]; + interlaced = height >> 15; + height &= 0x7fff; + + /* validate the info from the header */ + if (width > state->maxwidth || height > state->maxheight) + return AVCERR_VIDEO_TOO_LARGE; + if (channels > state->maxchannels) + return AVCERR_AUDIO_TOO_LARGE; + + /* validate that the sizes make sense */ + if (complength < 9 + 2 * channels) + return AVCERR_INVALID_DATA; + totalsize = 9 + 2 * channels + source[8]; + for (chnum = 0; chnum < channels; chnum++) + totalsize += (source[9 + 2 * chnum] << 8) | source[10 + 2 * chnum]; + if (totalsize >= complength) + return AVCERR_INVALID_DATA; + + /* starting offsets */ + srcoffs = 9 + 2 * channels; + dstoffs = 12; + + /* write the basics to the new header */ + dest[0] = 'c'; + dest[1] = 'h'; + dest[2] = 'a'; + dest[3] = 'v'; + dest[4] = metasize; + dest[5] = channels; + dest[6] = samples >> 8; + dest[7] = samples; + dest[8] = width >> 8; + dest[9] = width; + dest[10] = (interlaced << 7) | (height >> 8); + dest[11] = height; + + /* copy the metadata first */ + if (metasize > 0) + { + if ((state->decodemask & AVCOMP_DECODE_META) != 0) + memcpy(dest + dstoffs, source + srcoffs, metasize); + srcoffs += metasize; + dstoffs += metasize; + } + + /* decode the audio channels */ + if (channels > 0) + { + /* decode the audio */ + err = decode_audio(state, channels, samples, source + srcoffs, dest + dstoffs, &source[8], state->audioxor); + if (err != AVCERR_NONE) + return err; + + /* advance the pointers past the data */ + srcoffs += source[8]; + for (chnum = 0; chnum < channels; chnum++) + srcoffs += (source[9 + 2 * chnum] << 8) + source[10 + 2 * chnum]; + dstoffs += channels * samples * 2; + } + + /* decode the video data */ + if (width > 0 && height > 0 && (state->decodemask & AVCOMP_DECODE_VIDEO) != 0) + { + /* decode the video */ + err = decode_video(state, width, height, interlaced, source + srcoffs, complength - srcoffs, dest + dstoffs); + if (err != AVCERR_NONE) + return err; + } + return AVCERR_NONE; +} + + + +/*************************************************************************** + ENCODING HELPERS +***************************************************************************/ + +/*------------------------------------------------- + encode_audio - encode raw audio data + to the destination +-------------------------------------------------*/ + +static avcomp_error encode_audio(avcomp_state *state, int channels, int samples, const UINT8 *source, UINT8 *dest, UINT8 *sizes) +{ + UINT32 size, bytesleft; + huffman_error hufferr; + int chnum, sampnum; + const UINT8 *input; + UINT8 *output; + + /* get input/output pointers */ + input = source; + output = state->audiodata; + + /* extract audio deltas */ + for (chnum = 0; chnum < channels; chnum++) + { + INT16 prev = 0; + + for (sampnum = 0; sampnum < samples; sampnum++) + { + INT16 cursample, delta; + + /* read current sample (big endian) */ + cursample = *input++ << 8; + cursample |= *input++; + + /* compute delta against previous sample in this channel */ + delta = cursample - prev; + prev = cursample; + + /* store the delta (big endian) */ + *output++ = delta >> 8; + *output++ = delta & 0xff; + } + } + + /* reset our output pointer and huffman encode */ + input = state->audiodata; + output = dest; + bytesleft = channels * samples * 2; + + /* compute a single huffman tree for all the audio data */ + hufferr = huffman_compute_tree(state->audiocontext, input, bytesleft, 1); + if (hufferr != HUFFERR_NONE) + return AVCERR_COMPRESSION_ERROR; + hufferr = huffman_export_tree(state->audiocontext, output, bytesleft, &size); + if (hufferr != HUFFERR_NONE) + return AVCERR_COMPRESSION_ERROR; + output += size; + *sizes++ = size; + bytesleft -= size; + + /* encode each stream separately */ + for (chnum = 0; chnum < channels; chnum++) + { + /* encode the data */ + hufferr = huffman_encode_data(state->audiocontext, input, samples * 2, output, bytesleft, &size); + if (hufferr != HUFFERR_NONE) + return AVCERR_COMPRESSION_ERROR; + input += samples * 2; + output += size; + bytesleft = (bytesleft > size) ? (bytesleft - size) : 0; + + /* store the size of this stream */ + *sizes++ = size >> 8; + *sizes++ = size; + } + + /* if we didn't compress enough, set the huffman table size to 0 and memcpy the data */ + if (bytesleft == 0) + { + /* back up to the beginning */ + input = source; + output = dest; + sizes -= 1 + 2 * channels; + + /* huffman table size of 0 */ + *sizes++ = 0; + + /* write the full size of each stream and then copy it */ + size = samples * 2; + for (chnum = 0; chnum < channels; chnum++) + { + /* copy the data */ + memcpy(output, input, size); + input += size; + output += size; + + /* store the size of this stream */ + *sizes++ = size >> 8; + *sizes++ = size; + } + } + + return AVCERR_NONE; +} + + +/*------------------------------------------------- + encode_video - encode raw video data + to the destination +-------------------------------------------------*/ + +static avcomp_error encode_video(avcomp_state *state, int width, int height, int interlaced, const UINT8 *source, UINT8 *dest, UINT32 *complength) +{ + /* only lossless supported at this time */ + return encode_video_lossless(state, width, height, interlaced, source, dest, complength); +} + + +/*------------------------------------------------- + encode_video_lossless - do a lossless video + encoding using deltas and huffman encoding +-------------------------------------------------*/ + +static avcomp_error encode_video_lossless(avcomp_state *state, int width, int height, int interlaced, const UINT8 *source, UINT8 *dest, UINT32 *complength) +{ + UINT32 srcbytes = width * height * 2; + int rowbytes = width * 2; + huffman_error hufferr; + UINT32 outbytes; + UINT8 *output; + int x, y; + + /* loop over rows */ + output = state->deltadata; + for (y = 0; y < height; y++) + { + const UINT8 *src = &source[rowbytes * y]; + UINT8 lasty = (y == 0) ? 0x80 : src[-rowbytes + 1]; + UINT8 lastcb = (y == 0) ? 0x80 : src[-rowbytes + 0]; + UINT8 lastcr = (y == 0) ? 0x80 : src[-rowbytes + 2]; + + /* loop over columns */ + for (x = 0; x < rowbytes; x += 4) + { + *output++ = *src - lastcb; + lastcb = *src++; + *output++ = *src - lasty; + lasty = *src++; + *output++ = *src - lastcr; + lastcr = *src++; + *output++ = *src - lasty; + lasty = *src++; + } + } + + /* set up the output; first byte is 0x80 to indicate lossless encoding */ + output = dest; + *output++ = 0x80; + + /* now encode to the destination using two trees, one for the Y and one for the Cr/Cb */ + + /* compute the histograms for the data */ + hufferr = huffman_compute_tree(state->ycontext, state->deltadata + 0, srcbytes, 2); + if (hufferr != HUFFERR_NONE) + return AVCERR_COMPRESSION_ERROR; + hufferr = huffman_compute_tree(state->ccontext, state->deltadata + 1, srcbytes, 2); + if (hufferr != HUFFERR_NONE) + return AVCERR_COMPRESSION_ERROR; + + /* export the two trees to the data stream */ + hufferr = huffman_export_tree(state->ycontext, output, 256, &outbytes); + if (hufferr != HUFFERR_NONE) + return AVCERR_COMPRESSION_ERROR; + output += outbytes; + hufferr = huffman_export_tree(state->ccontext, output, 256, &outbytes); + if (hufferr != HUFFERR_NONE) + return AVCERR_COMPRESSION_ERROR; + output += outbytes; + + /* encode the data using the two trees */ + hufferr = huffman_encode_data_interleaved_2(state->ycontext, state->ccontext, state->deltadata, srcbytes, output, srcbytes, &outbytes); + if (hufferr != HUFFERR_NONE) + return AVCERR_COMPRESSION_ERROR; + output += outbytes; + + /* set the final length */ + *complength = output - dest; + return AVCERR_NONE; +} + + + +/*************************************************************************** + DECODING HELPERS +***************************************************************************/ + +/*------------------------------------------------- + decode_audio - decode audio from a + compressed data stream +-------------------------------------------------*/ + +static avcomp_error decode_audio(avcomp_state *state, int channels, int samples, const UINT8 *source, UINT8 *dest, const UINT8 *sizes, UINT32 destxor) +{ + huffman_error hufferr; + int chnum, sampnum; + const UINT8 *input; + UINT32 actsize; + UINT8 *output; + UINT16 size; + + /* set up input/output pointers */ + input = source; + output = dest; + + /* if no huffman length, just copy the data */ + size = *sizes++; + if (size == 0) + { + /* loop over channels */ + for (chnum = 0; chnum < channels; chnum++) + { + /* extract the size of this channel */ + size = *sizes++ << 8; + size |= *sizes++; + + /* copy the data */ + if ((state->decodemask & AVCOMP_DECODE_AUDIO(chnum)) != 0) + { + for (sampnum = 0; sampnum < samples; sampnum++) + { + output[0 ^ destxor] = input[0]; + output[1 ^ destxor] = input[1]; + output += 2; + input += 2; + } + } + else + { + input += size; + output += size; + } + } + return AVCERR_NONE; + } + + /* extract the huffman tree */ + input = source; + hufferr = huffman_import_tree(state->audiocontext, input, size, &actsize); + if (hufferr != HUFFERR_NONE || actsize != size) + return AVCERR_INVALID_DATA; + input += actsize; + + /* now loop over channels and decode their data */ + for (chnum = 0; chnum < channels; chnum++) + { + /* extract the size of this channel */ + size = *sizes++ << 8; + size |= *sizes++; + + /* decode the data */ + if ((state->decodemask & AVCOMP_DECODE_AUDIO(chnum)) != 0) + { + hufferr = huffman_decode_data(state->audiocontext, input, size, output, samples * 2, &actsize); + if (hufferr != HUFFERR_NONE || actsize != size) + return AVCERR_INVALID_DATA; + } + + /* advance */ + input += size; + output += samples * 2; + } + + /* reset input/output pointers */ + output = dest; + + /* reassemble audio from the deltas */ + for (chnum = 0; chnum < channels; chnum++) + { + INT16 prev = 0; + + if ((state->decodemask & AVCOMP_DECODE_AUDIO(chnum)) != 0) + { + for (sampnum = 0; sampnum < samples; sampnum++) + { + INT16 cursample; + + /* read current sample (big endian) */ + cursample = output[0] << 8; + cursample |= output[1]; + + /* compute delta against previous sample in this channel */ + cursample += prev; + prev = cursample; + + /* store the delta (big endian) */ + output[0 ^ destxor] = cursample >> 8; + output[1 ^ destxor] = cursample; + output += 2; + } + } + else + output += samples * 2; + } + return AVCERR_NONE; +} + + +/*------------------------------------------------- + decode_video - decode video from a + compressed data stream +-------------------------------------------------*/ + +static avcomp_error decode_video(avcomp_state *state, int width, int height, int interlaced, const UINT8 *source, UINT32 complength, UINT8 *dest) +{ + UINT32 targetstride, targetxor; + UINT8 *target; + + /* if we have a target buffer, use that instead */ + if (state->videobuffer != NULL) + { + target = state->videobuffer; + targetstride = state->videostride; + targetxor = state->videoxor; + } + else + { + target = dest; + targetstride = width * 2; + targetxor = 0; + } + + /* if the high bit of the first byte is set, we decode losslessly */ + if (source[0] & 0x80) + return decode_video_lossless(state, width, height, interlaced, source, complength, target, targetstride, targetxor); + else + return AVCERR_INVALID_DATA; +} + + +/*------------------------------------------------- + decode_video_lossless - do a lossless video + decoding using deltas and huffman encoding +-------------------------------------------------*/ + +static avcomp_error decode_video_lossless(avcomp_state *state, int width, int height, int interlaced, const UINT8 *source, UINT32 complength, UINT8 *dest, UINT32 deststride, UINT32 destxor) +{ + const UINT8 *sourceend = source + complength; + const huffman_lookup_value *table1, *table2; + huffman_error hufferr; + UINT32 bitbuf = 0; + UINT32 actsize; + int sbits = 0; + int x, y; + + /* skip the first byte */ + source++; + + /* decode the table and data */ + hufferr = huffman_import_tree(state->ycontext, source, sourceend - source, &actsize); + if (hufferr != HUFFERR_NONE) + return AVCERR_INVALID_DATA; + source += actsize; + hufferr = huffman_import_tree(state->ccontext, source, sourceend - source, &actsize); + if (hufferr != HUFFERR_NONE) + return AVCERR_INVALID_DATA; + source += actsize; + + /* get the lookup tables */ + hufferr = huffman_get_lookup_table(state->ycontext, &table1); + if (hufferr != HUFFERR_NONE) + return AVCERR_OUT_OF_MEMORY; + hufferr = huffman_get_lookup_table(state->ccontext, &table2); + if (hufferr != HUFFERR_NONE) + return AVCERR_OUT_OF_MEMORY; + + /* loop over rows */ + for (y = 0; y < height; y++) + { + UINT8 *dst = &dest[y * deststride]; + UINT8 lasty = (y == 0) ? 0x80 : (dst - deststride)[1 ^ destxor]; + UINT8 lastcb = (y == 0) ? 0x80 : (dst - deststride)[0 ^ destxor]; + UINT8 lastcr = (y == 0) ? 0x80 : (dst - deststride)[2 ^ destxor]; + + /* loop over columns */ + for (x = 0; x < width * 2; x += 4) + { + huffman_lookup_value lookup; + + /* keep the buffer full */ + while (sbits <= 24) + { + bitbuf |= *source++ << (24 - sbits); + sbits += 8; + } + + /* do the Cb component */ + lookup = table1[bitbuf >> 20]; + dst[(x + 0) ^ destxor] = lastcb += lookup >> 8; + lookup &= 0x1f; + bitbuf <<= lookup; + sbits -= lookup; + + /* do the Y component */ + lookup = table2[bitbuf >> 20]; + dst[(x + 1) ^ destxor] = lasty += lookup >> 8; + lookup &= 0x1f; + bitbuf <<= lookup; + sbits -= lookup; + + /* keep the buffer full */ + while (sbits <= 24) + { + bitbuf |= *source++ << (24 - sbits); + sbits += 8; + } + + /* do the Cr component */ + lookup = table1[bitbuf >> 20]; + dst[(x + 2) ^ destxor] = lastcr += lookup >> 8; + lookup &= 0x1f; + bitbuf <<= lookup; + sbits -= lookup; + + /* do the Y component */ + lookup = table2[bitbuf >> 20]; + dst[(x + 3) ^ destxor] = lasty += lookup >> 8; + lookup &= 0x1f; + bitbuf <<= lookup; + sbits -= lookup; + } + } + + return AVCERR_NONE; +} diff --git a/src/lib/util/avcomp.h b/src/lib/util/avcomp.h new file mode 100644 index 00000000000..9a729e3c1c1 --- /dev/null +++ b/src/lib/util/avcomp.h @@ -0,0 +1,60 @@ +/*************************************************************************** + + avcomp.h + + Audio/video compression and decompression helpers. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#ifndef __AVCOMP_H__ + +#include "osdcore.h" + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +/* errors */ +enum _avcomp_error +{ + AVCERR_NONE = 0, + AVCERR_INVALID_DATA, + AVCERR_VIDEO_TOO_LARGE, + AVCERR_AUDIO_TOO_LARGE, + AVCERR_OUT_OF_MEMORY, + AVCERR_COMPRESSION_ERROR +}; +typedef enum _avcomp_error avcomp_error; + +/* default decompression parameters */ +#define AVCOMP_DECODE_META (1 << 0) +#define AVCOMP_DECODE_VIDEO (1 << 1) +#define AVCOMP_DECODE_AUDIO(x) (1 << (2 + (x))) +#define AVCOMP_DECODE_DEFAULT (~0) + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +typedef struct _avcomp_state avcomp_state; + + + +/*************************************************************************** + PROTOTYPES +***************************************************************************/ + +avcomp_state *avcomp_init(UINT32 maxwidth, UINT32 maxheight, UINT32 maxchannels); +void avcomp_free(avcomp_state *state); +void avcomp_decompress_config(avcomp_state *state, UINT32 decodemask, UINT8 *videobuffer, UINT32 videostride, UINT32 videoxor, UINT32 audioxor); + +avcomp_error avcomp_encode_data(avcomp_state *state, const UINT8 *source, UINT8 *dest, UINT32 *complength); +avcomp_error avcomp_decode_data(avcomp_state *state, const UINT8 *source, UINT32 complength, UINT8 *dest); + +#endif diff --git a/src/lib/util/aviio.c b/src/lib/util/aviio.c new file mode 100644 index 00000000000..5fa464dcdb4 --- /dev/null +++ b/src/lib/util/aviio.c @@ -0,0 +1,2744 @@ +/*************************************************************************** + + aviio.c + + AVI movie format parsing helpers. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#include "aviio.h" + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define FILETYPE_READ 1 +#define FILETYPE_CREATE 2 + +#define MAX_RIFF_SIZE (2UL * 1024 * 1024 * 1024 - 1024) /* just under 2GB */ +#define MAX_AVI_SIZE_IN_GB (256) +#define FOUR_GB ((UINT64)1 << 32) + +#define MAX_SOUND_CHANNELS 2 +#define SOUND_BUFFER_MSEC 2000 /* microseconds of sond buffering */ + +#define CHUNKTYPE_RIFF AVI_FOURCC('R','I','F','F') +#define CHUNKTYPE_LIST AVI_FOURCC('L','I','S','T') +#define CHUNKTYPE_JUNK AVI_FOURCC('J','U','N','K') +#define CHUNKTYPE_AVIH AVI_FOURCC('a','v','i','h') +#define CHUNKTYPE_STRH AVI_FOURCC('s','t','r','h') +#define CHUNKTYPE_STRF AVI_FOURCC('s','t','r','f') +#define CHUNKTYPE_IDX1 AVI_FOURCC('i','d','x','1') +#define CHUNKTYPE_INDX AVI_FOURCC('i','n','d','x') +#define CHUNKTYPE_XXDB AVI_FOURCC(0x00,0x00,'d','b') +#define CHUNKTYPE_XXDC AVI_FOURCC(0x00,0x00,'d','c') +#define CHUNKTYPE_XXWB AVI_FOURCC(0x00,0x00,'w','b') +#define CHUNKTYPE_IXXX AVI_FOURCC('i','x',0x00,0x00) +#define CHUNKTYPE_XX_MASK AVI_FOURCC(0x00,0x00,0xff,0xff) + +#define LISTTYPE_AVI AVI_FOURCC('A','V','I',' ') +#define LISTTYPE_AVIX AVI_FOURCC('A','V','I','X') +#define LISTTYPE_HDRL AVI_FOURCC('h','d','r','l') +#define LISTTYPE_STRL AVI_FOURCC('s','t','r','l') +#define LISTTYPE_MOVI AVI_FOURCC('m','o','v','i') + +#define STREAMTYPE_VIDS AVI_FOURCC('v','i','d','s') +#define STREAMTYPE_AUDS AVI_FOURCC('a','u','d','s') + +#define HANDLER_DIB AVI_FOURCC('D','I','B',' ') +#define HANDLER_HFYU AVI_FOURCC('h','f','y','u') + +#define FORMAT_UYVY AVI_FOURCC('U','Y','V','Y') +#define FORMAT_VYUY AVI_FOURCC('V','Y','U','Y') +#define FORMAT_YUY2 AVI_FOURCC('Y','U','Y','2') +#define FORMAT_HFYU AVI_FOURCC('H','F','Y','U') + +/* main AVI header files */ +#define AVIF_HASINDEX 0x00000010 +#define AVIF_MUSTUSEINDEX 0x00000020 +#define AVIF_ISINTERLEAVED 0x00000100 +#define AVIF_COPYRIGHTED 0x00010000 +#define AVIF_WASCAPTUREFILE 0x00020000 + +/* index definitions */ +#define AVI_INDEX_OF_INDEXES 0x00 +#define AVI_INDEX_OF_CHUNKS 0x01 +#define AVI_INDEX_IS_DATA 0x80 +#define AVI_INDEX_2FIELD 0x01 + +/* HuffYUV definitions */ +#define HUFFYUV_PREDICT_LEFT 0 +#define HUFFYUV_PREDICT_GRADIENT 1 +#define HUFFYUV_PREDICT_MEDIAN 2 +#define HUFFYUV_PREDICT_DECORR 0x40 + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +typedef struct _avi_chunk avi_chunk; +struct _avi_chunk +{ + UINT64 offset; /* file offset of chunk header */ + UINT64 size; /* size of this chunk */ + UINT32 type; /* type of this chunk */ + UINT32 listtype; /* type of this list (if we are a list) */ +}; + + +typedef struct _avi_chunk_list avi_chunk_list; +struct _avi_chunk_list +{ + UINT64 offset; /* offset in the file of header */ + UINT32 length; /* length of the chunk including header */ +}; + + +typedef struct _huffyuv_table huffyuv_table; +struct _huffyuv_table +{ + UINT8 shift[256]; /* bit shift amounts */ + UINT32 bits[256]; /* bit match values */ + UINT32 mask[256]; /* bit mask values */ + UINT16 baselookup[65536]; /* base lookup table */ + UINT16 * extralookup; /* extra lookup tables */ +}; + + +typedef struct _huffyuv_data huffyuv_data; +struct _huffyuv_data +{ + UINT8 predictor; /* predictor */ + huffyuv_table table[3]; /* array of tables */ +}; + + +typedef struct _avi_stream avi_stream; +struct _avi_stream +{ + UINT32 type; /* subtype of stream */ + UINT32 format; /* format of stream data */ + + UINT32 rate; /* timescale for stream */ + UINT32 scale; /* duration of one sample in the stream */ + UINT32 samples; /* number of samples */ + + avi_chunk_list * chunk; /* list of chunks */ + UINT32 chunks; /* chunks currently known */ + UINT32 chunksalloc; /* number of chunks allocated */ + + UINT32 width; /* width of video */ + UINT32 height; /* height of video */ + UINT32 depth; /* depth of video */ + UINT8 interlace; /* interlace parameters */ + huffyuv_data * huffyuv; /* huffyuv decompression data */ + + UINT16 channels; /* audio channels */ + UINT16 samplebits; /* audio bits per sample */ + UINT32 samplerate; /* audio sample rate */ + + /* only used when creating */ + UINT64 saved_strh_offset; /* writeoffset of strh chunk */ + UINT64 saved_indx_offset; /* writeoffset of indx chunk */ +}; + + +struct _avi_file +{ + /* shared data */ + osd_file * file; /* pointer to open file */ + int type; /* type of access (read/create) */ + avi_movie_info info; /* movie info structure */ + UINT8 * tempbuffer; /* temporary buffer */ + UINT32 tempbuffersize; /* size of the temporary buffer */ + + /* only used when reading */ + int streams; /* number of streams */ + avi_stream * stream; /* allocated array of stream information */ + avi_chunk rootchunk; /* dummy root chunk that wraps the whole file */ + + /* only used when creating */ + UINT64 writeoffs; /* current file write offset */ + UINT64 riffbase; /* base of the current RIFF */ + + avi_chunk chunkstack[8]; /* stack of chunks we are writing */ + int chunksp; /* stack pointer for the current chunk */ + + UINT64 saved_movi_offset; /* writeoffset of movi list */ + UINT64 saved_avih_offset; /* writeoffset of avih chunk */ + + INT16 * soundbuf; /* buffer for sound data */ + UINT32 soundbuf_samples; /* length of sound buffer in samples */ + UINT32 soundbuf_chansamples[MAX_SOUND_CHANNELS]; /* samples in buffer for each channel */ + UINT32 soundbuf_chunks; /* number of chunks completed so far */ + UINT32 soundbuf_frames; /* number of frames ahead of the video */ +}; + + + +/*************************************************************************** + PROTOTYPES +***************************************************************************/ + +/* stream helpers */ + +/* core chunk read routines */ +static avi_error get_first_chunk(avi_file *file, const avi_chunk *parent, avi_chunk *newchunk); +static avi_error get_next_chunk(avi_file *file, const avi_chunk *parent, avi_chunk *newchunk); +static avi_error find_first_chunk(avi_file *file, UINT32 findme, const avi_chunk *container, avi_chunk *result); +static avi_error find_next_chunk(avi_file *file, UINT32 findme, const avi_chunk *container, avi_chunk *result); +static avi_error get_next_chunk_internal(avi_file *file, const avi_chunk *parent, avi_chunk *newchunk, UINT64 offset); +static avi_error read_chunk_data(avi_file *file, const avi_chunk *chunk, UINT8 **buffer); + +/* chunk read helpers */ +static avi_error read_movie_data(avi_file *file); +static avi_error extract_movie_info(avi_file *file); +static avi_error parse_avih_chunk(avi_file *file, avi_chunk *avih); +static avi_error parse_strh_chunk(avi_file *file, avi_stream *stream, avi_chunk *strh); +static avi_error parse_strf_chunk(avi_file *file, avi_stream *stream, avi_chunk *strf); +static avi_error parse_indx_chunk(avi_file *file, avi_stream *stream, avi_chunk *strf); +static avi_error parse_idx1_chunk(avi_file *file, UINT64 baseoffset, avi_chunk *idx1); + +/* core chunk write routines */ +static avi_error chunk_open(avi_file *file, UINT32 type, UINT32 listtype, UINT32 estlength); +static avi_error chunk_close(avi_file *file); +static avi_error chunk_write(avi_file *file, UINT32 type, const void *data, UINT32 length); +static avi_error chunk_overwrite(avi_file *file, UINT32 type, const void *data, UINT32 length, UINT64 *offset, int initial_write); + +/* chunk write helpers */ +static avi_error write_initial_headers(avi_file *file); +static avi_error write_avih_chunk(avi_file *file, int initial_write); +static avi_error write_strh_chunk(avi_file *file, avi_stream *stream, int initial_write); +static avi_error write_strf_chunk(avi_file *file, avi_stream *stream); +static avi_error write_indx_chunk(avi_file *file, avi_stream *stream, int initial_write); +static avi_error write_idx1_chunk(avi_file *file); + +/* sound buffering helpers */ +static avi_error soundbuf_initialize(avi_file *file); +static avi_error soundbuf_write_chunk(avi_file *file, UINT32 framenum); +static avi_error soundbuf_flush(avi_file *file, int only_flush_full); + +/* YUY helpers */ +static avi_error yuv_decompress_to_yuy16(avi_stream *stream, const UINT8 *data, UINT32 numbytes, bitmap_t *bitmap); +static avi_error yuy16_compress_to_yuy(avi_stream *stream, const bitmap_t *bitmap, UINT8 *data, UINT32 numbytes); + +/* HuffYUV helpers */ +static avi_error huffyuv_extract_tables(avi_stream *stream, const UINT8 *chunkdata, UINT32 size); +static avi_error huffyuv_decompress_to_yuy16(avi_stream *stream, const UINT8 *data, UINT32 numbytes, bitmap_t *bitmap); + +/* debugging */ +static void printf_chunk_recursive(avi_file *file, avi_chunk *chunk, int indent); + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + fetch_16bits - read 16 bits in LSB order + from the given buffer +-------------------------------------------------*/ + +INLINE UINT16 fetch_16bits(const UINT8 *data) +{ + return data[0] | (data[1] << 8); +} + + +/*------------------------------------------------- + fetch_32bits - read 32 bits in LSB order + from the given buffer +-------------------------------------------------*/ + +INLINE UINT32 fetch_32bits(const UINT8 *data) +{ + return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); +} + + +/*------------------------------------------------- + fetch_64bits - read 64 bits in LSB order + from the given buffer +-------------------------------------------------*/ + +INLINE UINT64 fetch_64bits(const UINT8 *data) +{ + return (UINT64)data[0] | ((UINT64)data[1] << 8) | + ((UINT64)data[2] << 16) | ((UINT64)data[3] << 24) | + ((UINT64)data[4] << 32) | ((UINT64)data[5] << 40) | + ((UINT64)data[6] << 48) | ((UINT64)data[7] << 56); +} + + +/*------------------------------------------------- + put_16bits - write 16 bits in LSB order + to the given buffer +-------------------------------------------------*/ + +INLINE void put_16bits(UINT8 *data, UINT16 value) +{ + data[0] = value >> 0; + data[1] = value >> 8; +} + + +/*------------------------------------------------- + put_32bits - write 32 bits in LSB order + to the given buffer +-------------------------------------------------*/ + +INLINE void put_32bits(UINT8 *data, UINT32 value) +{ + data[0] = value >> 0; + data[1] = value >> 8; + data[2] = value >> 16; + data[3] = value >> 24; +} + + +/*------------------------------------------------- + put_64bits - write 64 bits in LSB order + to the given buffer +-------------------------------------------------*/ + +INLINE void put_64bits(UINT8 *data, UINT64 value) +{ + data[0] = value >> 0; + data[1] = value >> 8; + data[2] = value >> 16; + data[3] = value >> 24; + data[4] = value >> 32; + data[5] = value >> 40; + data[6] = value >> 48; + data[7] = value >> 56; +} + + +/*------------------------------------------------- + get_video_stream - return a pointer to the + video stream +-------------------------------------------------*/ + +INLINE avi_stream *get_video_stream(avi_file *file) +{ + int streamnum; + + /* find the video stream */ + for (streamnum = 0; streamnum < file->streams; streamnum++) + if (file->stream[streamnum].type == STREAMTYPE_VIDS) + return &file->stream[streamnum]; + + return NULL; +} + + +/*------------------------------------------------- + get_audio_stream - return a pointer to the + audio stream for the 'n'th channel +-------------------------------------------------*/ + +INLINE avi_stream *get_audio_stream(avi_file *file, int channel, int *offset) +{ + int streamnum; + + /* find the audios stream */ + for (streamnum = 0; streamnum < file->streams; streamnum++) + if (file->stream[streamnum].type == STREAMTYPE_AUDS) + { + if (channel < file->stream[streamnum].channels) + { + if (offset != NULL) + *offset = channel; + return &file->stream[streamnum]; + } + channel -= file->stream[streamnum].channels; + } + + return NULL; +} + + +/*------------------------------------------------- + set_stream_chunk_info - set the chunk info + for a given chunk within a stream +-------------------------------------------------*/ + +INLINE avi_error set_stream_chunk_info(avi_stream *stream, UINT32 index, UINT64 offset, UINT32 length) +{ + /* if we need to allocate more, allocate more */ + if (index >= stream->chunksalloc) + { + UINT32 newcount = MAX(index, stream->chunksalloc + 1000); + stream->chunk = realloc(stream->chunk, newcount * sizeof(stream->chunk[0])); + if (stream->chunk == NULL) + return AVIERR_NO_MEMORY; + stream->chunksalloc = newcount; + } + + /* set the data */ + stream->chunk[index].offset = offset; + stream->chunk[index].length = length; + + /* update the number of chunks */ + stream->chunks = MAX(stream->chunks, index + 1); + return AVIERR_NONE; +} + + +/*------------------------------------------------- + compute_idx1_size - compute the size of the + idx1 chunk +-------------------------------------------------*/ + +INLINE UINT32 compute_idx1_size(avi_file *file) +{ + int chunks = 0; + int strnum; + + /* count chunks in streams */ + for (strnum = 0; strnum < file->streams; strnum++) + chunks += file->stream[strnum].chunks; + + return chunks * 16 + 8; +} + + +/*------------------------------------------------- + get_chunkid_for_stream - make a chunk id for + a given stream +-------------------------------------------------*/ + +INLINE UINT32 get_chunkid_for_stream(avi_file *file, avi_stream *stream) +{ + UINT32 chunkid; + + chunkid = AVI_FOURCC('0' + (stream - file->stream) / 10, '0' + (stream - file->stream) % 10, 0, 0); + if (stream->type == STREAMTYPE_VIDS) + chunkid |= (stream->format == 0) ? CHUNKTYPE_XXDB : CHUNKTYPE_XXDC; + else if (stream->type == STREAMTYPE_AUDS) + chunkid |= CHUNKTYPE_XXWB; + + return chunkid; +} + + +/*------------------------------------------------- + framenum_to_samplenum - given a video frame + number, get the first sample number +-------------------------------------------------*/ + +INLINE UINT32 framenum_to_samplenum(avi_file *file, UINT32 framenum) +{ + return ((UINT64)file->info.audio_samplerate * (UINT64)framenum * (UINT64)file->info.video_sampletime + file->info.video_timescale - 1) / (UINT64)file->info.video_timescale; +} + + +/*------------------------------------------------- + expand_tempbuffer - expand the file's + tempbuffer if necessary to contain the + requested amount of data +-------------------------------------------------*/ + +INLINE avi_error expand_tempbuffer(avi_file *file, UINT32 length) +{ + /* expand the tempbuffer to hold the data if necessary */ + if (length > file->tempbuffersize) + { + file->tempbuffersize = 2 * length; + file->tempbuffer = realloc(file->tempbuffer, file->tempbuffersize); + if (file->tempbuffer == NULL) + return AVIERR_NO_MEMORY; + } + return AVIERR_NONE; +} + + + +/*************************************************************************** + IMPLEMENTATION +***************************************************************************/ + +/*------------------------------------------------- + avi_open - open an AVI movie file for read +-------------------------------------------------*/ + +avi_error avi_open(const char *filename, avi_file **file) +{ + avi_file *newfile = NULL; + file_error filerr; + avi_error avierr; + UINT64 length; + + /* allocate the file */ + newfile = malloc(sizeof(*newfile)); + if (newfile == NULL) + return AVIERR_NO_MEMORY; + memset(newfile, 0, sizeof(*newfile)); + newfile->type = FILETYPE_READ; + + /* open the file */ + filerr = osd_open(filename, OPEN_FLAG_READ, &newfile->file, &length); + if (filerr != FILERR_NONE) + { + avierr = AVIERR_CANT_OPEN_FILE; + goto error; + } + + /* make a root atom */ + newfile->rootchunk.offset = 0; + newfile->rootchunk.size = length; + newfile->rootchunk.type = 0; + newfile->rootchunk.listtype = 0; + + /* parse the data */ + avierr = read_movie_data(newfile); + if (avierr != AVIERR_NONE) + goto error; + + *file = newfile; + return AVIERR_NONE; + +error: + /* clean up after an error */ + if (newfile != NULL) + { + if (newfile->file != NULL) + osd_close(newfile->file); + free(newfile); + } + return avierr; +} + + +/*------------------------------------------------- + avi_create - create a new QuickTime movie file +-------------------------------------------------*/ + +avi_error avi_create(const char *filename, const avi_movie_info *info, avi_file **file) +{ + avi_file *newfile = NULL; + file_error filerr; + avi_stream *stream; + avi_error avierr; + UINT64 length; + + /* validate video info */ + if ((info->video_format != 0 && info->video_format != FORMAT_UYVY && info->video_format != FORMAT_VYUY && info->video_format != FORMAT_YUY2) || + info->video_width == 0 || + info->video_height == 0 || + info->video_depth == 0 || info->video_depth % 8 != 0) + return AVIERR_UNSUPPORTED_VIDEO_FORMAT; + + /* validate audio info */ + if (info->audio_format != 0 || + info->audio_channels > MAX_SOUND_CHANNELS || + info->audio_samplebits != 16) + return AVIERR_UNSUPPORTED_AUDIO_FORMAT; + + /* allocate the file */ + newfile = malloc(sizeof(*newfile)); + if (newfile == NULL) + return AVIERR_NO_MEMORY; + memset(newfile, 0, sizeof(*newfile)); + newfile->type = FILETYPE_CREATE; + + /* open the file */ + filerr = osd_open(filename, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS, &newfile->file, &length); + if (filerr != FILERR_NONE) + { + avierr = AVIERR_CANT_OPEN_FILE; + goto error; + } + + /* copy the movie info */ + newfile->info = *info; + newfile->info.video_numsamples = 0; + newfile->info.audio_numsamples = 0; + + /* allocate two streams */ + newfile->stream = malloc(2 * sizeof(newfile->stream[0])); + if (newfile->stream == NULL) + { + avierr = AVIERR_NO_MEMORY; + goto error; + } + memset(newfile->stream, 0, 2 * sizeof(newfile->stream[0])); + + /* initialize the video track */ + stream = &newfile->stream[newfile->streams++]; + stream->type = STREAMTYPE_VIDS; + stream->format = newfile->info.video_format; + stream->rate = newfile->info.video_timescale; + stream->scale = newfile->info.video_sampletime; + stream->width = newfile->info.video_width; + stream->height = newfile->info.video_height; + stream->depth = newfile->info.video_depth; + + /* initialize the audio track */ + if (newfile->info.audio_channels > 0) + { + stream = &newfile->stream[newfile->streams++]; + stream->type = STREAMTYPE_AUDS; + stream->format = newfile->info.audio_format; + stream->rate = newfile->info.audio_timescale; + stream->scale = newfile->info.audio_sampletime; + stream->channels = newfile->info.audio_channels; + stream->samplebits = newfile->info.audio_samplebits; + stream->samplerate = newfile->info.audio_samplerate; + + /* initialize the sound buffering */ + avierr = soundbuf_initialize(newfile); + if (avierr != AVIERR_NONE) + goto error; + } + + /* write the initial headers */ + avierr = write_initial_headers(newfile); + + *file = newfile; + return AVIERR_NONE; + +error: + /* clean up after an error */ + if (newfile != NULL) + { + if (newfile->stream != NULL) + free(newfile->stream); + if (newfile->file != NULL) + { + osd_close(newfile->file); + osd_rmfile(filename); + } + free(newfile); + } + return avierr; +} + + +/*------------------------------------------------- + avi_close - close a QuickTime movie file +-------------------------------------------------*/ + +avi_error avi_close(avi_file *file) +{ + avi_error avierr = AVIERR_NONE; + int strnum; + + /* if we're creating a new file, finalize it by writing out the non-media chunks */ + if (file->type == FILETYPE_CREATE) + { + /* flush any pending sound data */ + avierr = soundbuf_flush(file, FALSE); + + /* close the movi chunk */ + if (avierr == AVIERR_NONE) + avierr = chunk_close(file); + + /* if this is the first RIFF chunk, write an idx1 */ + if (avierr == AVIERR_NONE && file->riffbase == 0) + avierr = write_idx1_chunk(file); + + /* close the RIFF chunk */ + if (avierr == AVIERR_NONE) + avierr = chunk_close(file); + + /* update the avih chunk */ + if (avierr == AVIERR_NONE) + avierr = write_avih_chunk(file, FALSE); + + /* update the strh and indx chunks for each stream */ + for (strnum = 0; strnum < file->streams; strnum++) + { + if (avierr == AVIERR_NONE) + avierr = write_strh_chunk(file, &file->stream[strnum], FALSE); + if (avierr == AVIERR_NONE) + avierr = write_indx_chunk(file, &file->stream[strnum], FALSE); + } + } + + /* close the file */ + osd_close(file->file); + + /* free the stream-specific data */ + for (strnum = 0; strnum < file->streams; strnum++) + { + avi_stream *stream = &file->stream[strnum]; + if (stream->huffyuv != NULL) + { + huffyuv_data *huffyuv = stream->huffyuv; + int table; + + for (table = 0; table < ARRAY_LENGTH(huffyuv->table); table++) + if (huffyuv->table[table].extralookup != NULL) + free(huffyuv->table[table].extralookup); + free(huffyuv); + } + if (stream->chunk != NULL) + free(stream->chunk); + } + + /* free the file itself */ + if (file->soundbuf != NULL) + free(file->soundbuf); + if (file->stream != NULL) + free(file->stream); + if (file->tempbuffer != NULL) + free(file->tempbuffer); + free(file); + return avierr; +} + + +/*------------------------------------------------- + avi_printf_chunks - print the chunks in a file +-------------------------------------------------*/ + +void avi_printf_chunks(avi_file *file) +{ + printf_chunk_recursive(file, &file->rootchunk, 0); +} + + +/*------------------------------------------------- + avi_error_string - get the error string for + an avi_error +-------------------------------------------------*/ + +const char *avi_error_string(avi_error err) +{ + switch (err) + { + case AVIERR_NONE: return "success"; + case AVIERR_END: return "hit end of file"; + case AVIERR_INVALID_DATA: return "invalid data"; + case AVIERR_NO_MEMORY: return "out of memory"; + case AVIERR_READ_ERROR: return "read error"; + case AVIERR_WRITE_ERROR: return "write error"; + case AVIERR_STACK_TOO_DEEP: return "stack overflow"; + case AVIERR_UNSUPPORTED_FEATURE: return "unsupported feature"; + case AVIERR_CANT_OPEN_FILE: return "unable to open file"; + case AVIERR_INCOMPATIBLE_AUDIO_STREAMS: return "found incompatible audio streams"; + case AVIERR_INVALID_SAMPLERATE: return "found invalid sample rate"; + case AVIERR_INVALID_STREAM: return "invalid stream"; + case AVIERR_INVALID_FRAME: return "invalid frame index"; + case AVIERR_INVALID_BITMAP: return "invalid bitmap"; + case AVIERR_UNSUPPORTED_VIDEO_FORMAT: return "unsupported video format"; + case AVIERR_UNSUPPORTED_AUDIO_FORMAT: return "unsupported audio format"; + case AVIERR_EXCEEDED_SOUND_BUFFER: return "sound buffer overflow"; + default: return "undocumented error"; + } +} + + +/*------------------------------------------------- + avi_get_movie_info - return a pointer to the + movie info +-------------------------------------------------*/ + +const avi_movie_info *avi_get_movie_info(avi_file *file) +{ + return &file->info; +} + + +/*------------------------------------------------- + avi_frame_to_sample - convert a frame index + to a sample index +-------------------------------------------------*/ + +UINT32 avi_first_sample_in_frame(avi_file *file, UINT32 framenum) +{ + return framenum_to_samplenum(file, framenum); +} + + +/*------------------------------------------------- + avi_read_video_frame_yuy16 - read video data + for a particular frame from the AVI file, + converting to YUY16 format +-------------------------------------------------*/ + +avi_error avi_read_video_frame_yuy16(avi_file *file, UINT32 framenum, bitmap_t *bitmap) +{ + avi_error avierr = AVIERR_NONE; + UINT32 bytes_read, chunkid; + file_error filerr; + avi_stream *stream; + + /* get the video stream */ + stream = get_video_stream(file); + if (stream == NULL) + return AVIERR_INVALID_STREAM; + + /* validate our ability to handle the data */ + if (stream->format != FORMAT_UYVY && stream->format != FORMAT_VYUY && stream->format != FORMAT_YUY2 && stream->format != FORMAT_HFYU) + return AVIERR_UNSUPPORTED_VIDEO_FORMAT; + + /* assume one chunk == one frame */ + if (framenum >= stream->chunks) + return AVIERR_INVALID_FRAME; + + /* we only support YUY-style bitmaps (16bpp) */ + if (bitmap->format != BITMAP_FORMAT_YUY16 || bitmap->width < stream->width || bitmap->height < stream->height) + return AVIERR_INVALID_BITMAP; + + /* expand the tempbuffer to hold the data if necessary */ + avierr = expand_tempbuffer(file, stream->chunk[framenum].length); + if (avierr != AVIERR_NONE) + return avierr; + + /* read in the data */ + filerr = osd_read(file->file, file->tempbuffer, stream->chunk[framenum].offset, stream->chunk[framenum].length, &bytes_read); + if (filerr != FILERR_NONE || bytes_read != stream->chunk[framenum].length) + return AVIERR_READ_ERROR; + + /* validate this is good data */ + chunkid = fetch_32bits(&file->tempbuffer[0]); + if (chunkid == get_chunkid_for_stream(file, stream)) + { + /* HuffYUV-compressed */ + if (stream->format == FORMAT_HFYU) + avierr = huffyuv_decompress_to_yuy16(stream, file->tempbuffer + 8, stream->chunk[framenum].length - 8, bitmap); + + /* other YUV-compressed */ + else + avierr = yuv_decompress_to_yuy16(stream, file->tempbuffer + 8, stream->chunk[framenum].length - 8, bitmap); + } + else + avierr = AVIERR_INVALID_DATA; + + return avierr; +} + + +/*------------------------------------------------- + avi_read_sound_samples - read sound sample + data from an AVI file +-------------------------------------------------*/ + +avi_error avi_read_sound_samples(avi_file *file, int channel, UINT32 firstsample, UINT32 numsamples, INT16 *output) +{ + avi_error avierr = AVIERR_NONE; + UINT32 bytes_per_sample; + file_error filerr; + avi_stream *stream; + int offset; + + /* get the audio stream */ + stream = get_audio_stream(file, channel, &offset); + if (stream == NULL) + return AVIERR_INVALID_STREAM; + + /* validate our ability to handle the data */ + if (stream->format != 0 || (stream->samplebits != 8 && stream->samplebits != 16)) + return AVIERR_UNSUPPORTED_AUDIO_FORMAT; + + /* verify we are in range */ + if (firstsample >= stream->samples) + return AVIERR_INVALID_FRAME; + if (firstsample + numsamples > stream->samples) + numsamples = stream->samples - firstsample; + + /* determine bytes per sample */ + bytes_per_sample = (stream->samplebits / 8) * stream->channels; + + /* loop until all samples have been extracted */ + while (numsamples > 0) + { + UINT32 chunkbase = 0, chunkend = 0, chunkid; + UINT32 bytes_read, samples_this_chunk; + int chunknum, sampnum; + + /* locate the chunk with the first sample */ + for (chunknum = 0; chunknum < stream->chunks; chunknum++) + { + chunkend = chunkbase + (stream->chunk[chunknum].length - 8) / bytes_per_sample; + if (firstsample < chunkend) + break; + chunkbase = chunkend; + } + + /* expand the tempbuffer to hold the data if necessary */ + avierr = expand_tempbuffer(file, stream->chunk[chunknum].length); + if (avierr != AVIERR_NONE) + return avierr; + + /* read in the data */ + filerr = osd_read(file->file, file->tempbuffer, stream->chunk[chunknum].offset, stream->chunk[chunknum].length, &bytes_read); + if (filerr != FILERR_NONE || bytes_read != stream->chunk[chunknum].length) + return AVIERR_READ_ERROR; + + /* validate this is good data */ + chunkid = fetch_32bits(&file->tempbuffer[0]); + if (chunkid != get_chunkid_for_stream(file, stream)) + return AVIERR_INVALID_DATA; + + /* determine how many samples to copy */ + samples_this_chunk = chunkend - firstsample; + samples_this_chunk = MIN(samples_this_chunk, numsamples); + + /* extract 16-bit samples from the chunk */ + if (stream->samplebits == 16) + { + const INT16 *base = (const INT16 *)(file->tempbuffer + 8); + base += stream->channels * (firstsample - chunkbase) + offset; + for (sampnum = 0; sampnum < samples_this_chunk; sampnum++) + { + *output++ = LITTLE_ENDIANIZE_INT16(*base); + base += stream->channels; + } + } + + /* extract 8-bit samples from the chunk */ + else if (stream->samplebits == 8) + { + const UINT8 *base = (const UINT8 *)(file->tempbuffer + 8); + base += stream->channels * (firstsample - chunkbase) + offset; + for (sampnum = 0; sampnum < samples_this_chunk; sampnum++) + { + *output++ = (*base << 8) - 0x8000; + base += stream->channels; + } + } + + /* update our counters */ + firstsample += samples_this_chunk; + numsamples -= samples_this_chunk; + } + + return avierr; +} + + +/*------------------------------------------------- + avi_append_video_frame_yuy16 - append a frame + of video in YUY16 format +-------------------------------------------------*/ + +avi_error avi_append_video_frame_yuy16(avi_file *file, const bitmap_t *bitmap) +{ + avi_stream *stream = get_video_stream(file); + avi_error avierr; + UINT32 maxlength; + + /* double check bitmap format */ + if (bitmap->format != BITMAP_FORMAT_YUY16) + return AVIERR_INVALID_BITMAP; + + /* write out any sound data first */ + avierr = soundbuf_write_chunk(file, stream->chunks); + if (avierr != AVIERR_NONE) + return avierr; + + /* make sure we have enough room */ + maxlength = 2 * stream->width * stream->height; + avierr = expand_tempbuffer(file, maxlength); + if (avierr != AVIERR_NONE) + return avierr; + + /* now compress the data */ + avierr = yuy16_compress_to_yuy(stream, bitmap, file->tempbuffer, maxlength); + if (avierr != AVIERR_NONE) + return avierr; + + /* set the info for this new chunk */ + avierr = set_stream_chunk_info(stream, stream->chunks, file->writeoffs, maxlength + 8); + if (avierr != AVIERR_NONE) + return avierr; + stream->samples = file->info.video_numsamples = stream->chunks; + + /* write the data */ + return chunk_write(file, get_chunkid_for_stream(file, stream), file->tempbuffer, maxlength); +} + + +/*------------------------------------------------- + avi_append_sound_samples - append sound + samples +-------------------------------------------------*/ + +avi_error avi_append_sound_samples(avi_file *file, int channel, const INT16 *samples, UINT32 numsamples) +{ + UINT32 sampoffset = file->soundbuf_chansamples[channel]; + UINT32 sampnum; + + /* see if we have enough room in the buffer */ + if (sampoffset + numsamples > file->soundbuf_samples) + return AVIERR_EXCEEDED_SOUND_BUFFER; + + /* append samples to the buffer in little-endian format */ + for (sampnum = 0; sampnum < numsamples; sampnum++) + { + INT16 data = *samples++; + data = LITTLE_ENDIANIZE_INT16(data); + file->soundbuf[sampoffset++ * file->info.audio_channels + channel] = data; + } + file->soundbuf_chansamples[channel] = sampoffset; + + /* flush any full sound chunks to disk */ + return soundbuf_flush(file, TRUE); +} + + +/*------------------------------------------------- + read_chunk_data - read a chunk's data into + memory +-------------------------------------------------*/ + +static avi_error read_chunk_data(avi_file *file, const avi_chunk *chunk, UINT8 **buffer) +{ + file_error filerr; + UINT32 bytes_read; + + /* allocate memory for the data */ + *buffer = malloc(chunk->size); + if (*buffer == NULL) + return AVIERR_NO_MEMORY; + + /* read from the file */ + filerr = osd_read(file->file, *buffer, chunk->offset + 8, chunk->size, &bytes_read); + if (filerr != FILERR_NONE || bytes_read != chunk->size) + { + free(*buffer); + *buffer = NULL; + return AVIERR_READ_ERROR; + } + + return AVIERR_NONE; +} + + +/*------------------------------------------------- + get_first_chunk - get information about the + first chunk in a container +-------------------------------------------------*/ + +static avi_error get_first_chunk(avi_file *file, const avi_chunk *parent, avi_chunk *newchunk) +{ + UINT64 startoffset = (parent != NULL && parent->type != 0) ? parent->offset + 12 : 0; + if (parent != NULL && parent->type != CHUNKTYPE_LIST && parent->type != CHUNKTYPE_RIFF && parent->type != 0) + return AVIERR_INVALID_DATA; + return get_next_chunk_internal(file, parent, newchunk, startoffset); +} + + +/*------------------------------------------------- + get_next_chunk - get information about the + next chunk in a container +-------------------------------------------------*/ + +static avi_error get_next_chunk(avi_file *file, const avi_chunk *parent, avi_chunk *newchunk) +{ + UINT64 nextoffset = newchunk->offset + 8 + newchunk->size + (newchunk->size & 1); + return get_next_chunk_internal(file, parent, newchunk, nextoffset); +} + + +/*------------------------------------------------- + find_first_chunk - get information about the + first chunk of a particular type in a container +-------------------------------------------------*/ + +static avi_error find_first_chunk(avi_file *file, UINT32 findme, const avi_chunk *container, avi_chunk *result) +{ + avi_error avierr; + + for (avierr = get_first_chunk(file, container, result); avierr == AVIERR_NONE; avierr = get_next_chunk(file, container, result)) + if (result->type == findme) + return AVIERR_NONE; + + return avierr; +} + + +/*------------------------------------------------- + find_next_chunk - get information about the + next chunk of a particular type in a container +-------------------------------------------------*/ + +static avi_error find_next_chunk(avi_file *file, UINT32 findme, const avi_chunk *container, avi_chunk *result) +{ + avi_error avierr; + + for (avierr = get_next_chunk(file, container, result); avierr == AVIERR_NONE; avierr = get_next_chunk(file, container, result)) + if (result->type == findme) + return AVIERR_NONE; + + return avierr; +} + + +/*------------------------------------------------- + find_first_list - get information about the + first list of a particular type in a container +-------------------------------------------------*/ + +static avi_error find_first_list(avi_file *file, UINT32 findme, const avi_chunk *container, avi_chunk *result) +{ + avi_error avierr; + + for (avierr = find_first_chunk(file, CHUNKTYPE_LIST, container, result); avierr == AVIERR_NONE; avierr = find_next_chunk(file, CHUNKTYPE_LIST, container, result)) + if (result->listtype == findme) + return AVIERR_NONE; + + return avierr; +} + + +/*------------------------------------------------- + find_next_list - get information about the + next list of a particular type in a container +-------------------------------------------------*/ + +static avi_error find_next_list(avi_file *file, UINT32 findme, const avi_chunk *container, avi_chunk *result) +{ + avi_error avierr; + + for (avierr = find_next_chunk(file, CHUNKTYPE_LIST, container, result); avierr == AVIERR_NONE; avierr = find_next_chunk(file, CHUNKTYPE_LIST, container, result)) + if (result->listtype == findme) + return AVIERR_NONE; + + return avierr; +} + + +/*------------------------------------------------- + get_next_chunk_internal - fetch the next + chunk relative to the current one +-------------------------------------------------*/ + +static avi_error get_next_chunk_internal(avi_file *file, const avi_chunk *parent, avi_chunk *newchunk, UINT64 offset) +{ + file_error filerr; + UINT8 buffer[12]; + UINT32 bytesread; + + /* NULL parent implies the root */ + if (parent == NULL) + parent = &file->rootchunk; + + /* start at the current offset */ + newchunk->offset = offset; + + /* if we're past the bounds of the parent, bail */ + if (newchunk->offset + 8 >= parent->offset + 8 + parent->size) + return AVIERR_END; + + /* read the header */ + filerr = osd_read(file->file, buffer, newchunk->offset, 8, &bytesread); + if (filerr != FILERR_NONE || bytesread != 8) + return AVIERR_INVALID_DATA; + + /* fill in the new chunk */ + newchunk->type = fetch_32bits(&buffer[0]); + newchunk->size = fetch_32bits(&buffer[4]); + + /* if we are a list, fetch the list type */ + if (newchunk->type == CHUNKTYPE_LIST || newchunk->type == CHUNKTYPE_RIFF) + { + filerr = osd_read(file->file, &buffer[8], newchunk->offset + 8, 4, &bytesread); + if (filerr != FILERR_NONE || bytesread != 4) + return AVIERR_INVALID_DATA; + newchunk->listtype = fetch_32bits(&buffer[8]); + } + + return AVIERR_NONE; +} + + +/*------------------------------------------------- + read_movie_data - get data about a movie +-------------------------------------------------*/ + +static avi_error read_movie_data(avi_file *file) +{ + avi_chunk riff, hdrl, avih, strl, strh, strf, indx, movi, idx1; + avi_error avierr; + int strindex; + + /* find the RIFF chunk */ + avierr = find_first_chunk(file, CHUNKTYPE_RIFF, NULL, &riff); + if (avierr != AVIERR_NONE) + goto error; + + /* verify that the RIFF type is AVI */ + if (riff.listtype != LISTTYPE_AVI) + { + avierr = AVIERR_INVALID_DATA; + goto error; + } + + /* find the hdrl LIST chunk within the RIFF */ + avierr = find_first_list(file, LISTTYPE_HDRL, &riff, &hdrl); + if (avierr != AVIERR_NONE) + goto error; + + /* find the avih chunk */ + avierr = find_first_chunk(file, CHUNKTYPE_AVIH, &hdrl, &avih); + if (avierr != AVIERR_NONE) + goto error; + + /* parse the avih chunk */ + avierr = parse_avih_chunk(file, &avih); + if (avierr != AVIERR_NONE) + goto error; + + /* loop over strl LIST chunks */ + strindex = 0; + for (avierr = find_first_list(file, LISTTYPE_STRL, &hdrl, &strl); avierr == AVIERR_NONE; avierr = find_next_list(file, LISTTYPE_STRL, &hdrl, &strl)) + { + /* if we have too many, it's a bad file */ + if (strindex >= file->streams) + goto error; + + /* find the strh chunk */ + avierr = find_first_chunk(file, CHUNKTYPE_STRH, &strl, &strh); + if (avierr != AVIERR_NONE) + goto error; + + /* parse the data */ + avierr = parse_strh_chunk(file, &file->stream[strindex], &strh); + if (avierr != AVIERR_NONE) + goto error; + + /* find the strf chunk */ + avierr = find_first_chunk(file, CHUNKTYPE_STRF, &strl, &strf); + if (avierr != AVIERR_NONE) + goto error; + + /* parse the data */ + avierr = parse_strf_chunk(file, &file->stream[strindex], &strf); + if (avierr != AVIERR_NONE) + goto error; + + /* find the indx chunk, if present */ + avierr = find_first_chunk(file, CHUNKTYPE_INDX, &strl, &indx); + if (avierr == AVIERR_NONE) + avierr = parse_indx_chunk(file, &file->stream[strindex], &indx); + + /* next stream */ + strindex++; + } + + /* normalize the error after parsing the stream headers */ + if (avierr == AVIERR_END) + avierr = AVIERR_NONE; + if (avierr != AVIERR_NONE) + goto error; + + /* find the base of the movi data */ + avierr = find_first_list(file, LISTTYPE_MOVI, &riff, &movi); + if (avierr != AVIERR_NONE) + goto error; + + /* find and parse the idx1 chunk within the RIFF (if present) */ + avierr = find_first_chunk(file, CHUNKTYPE_IDX1, &riff, &idx1); + if (avierr == AVIERR_NONE) + avierr = parse_idx1_chunk(file, movi.offset + 8, &idx1); + avierr = AVIERR_NONE; + + /* now extract the movie info */ + avierr = extract_movie_info(file); + +error: + return avierr; +} + + +/*------------------------------------------------- + extract_movie_info - extract the movie info + from the streams we've read +-------------------------------------------------*/ + +static avi_error extract_movie_info(avi_file *file) +{ + avi_stream *audiostream; + avi_stream *stream; + + /* get the video stream */ + stream = get_video_stream(file); + if (stream != NULL) + { + /* fill in the info */ + file->info.video_format = stream->format; + file->info.video_timescale = stream->rate; + file->info.video_sampletime = stream->scale; + file->info.video_numsamples = stream->samples; + file->info.video_width = stream->width; + file->info.video_height = stream->height; + } + + /* get the first audio stream */ + stream = get_audio_stream(file, 0, NULL); + if (stream != NULL) + { + /* fill in the info */ + file->info.audio_format = stream->format; + file->info.audio_timescale = stream->rate; + file->info.audio_sampletime = stream->scale; + file->info.audio_numsamples = stream->samples; + file->info.audio_channels = 1; + file->info.audio_samplebits = stream->samplebits; + file->info.audio_samplerate = stream->samplerate; + } + + /* now make sure all other audio streams are valid */ + audiostream = stream; + while (1) + { + /* get the stream info */ + stream = get_audio_stream(file, file->info.audio_channels, NULL); + if (stream == NULL) + break; + file->info.audio_channels++; + + /* verify compatibility */ + if (file->info.audio_format != stream->format || + file->info.audio_timescale != stream->rate || + file->info.audio_sampletime != stream->scale || + file->info.audio_numsamples != stream->samples || + file->info.audio_samplebits != stream->samplebits || + file->info.audio_samplerate != stream->samplerate) + return AVIERR_INCOMPATIBLE_AUDIO_STREAMS; + } + + return AVIERR_NONE; +} + + +/*------------------------------------------------- + parse_avih_chunk - parse an avih header + chunk +-------------------------------------------------*/ + +static avi_error parse_avih_chunk(avi_file *file, avi_chunk *avih) +{ + UINT8 *chunkdata = NULL; + avi_error avierr; + + /* read the data */ + avierr = read_chunk_data(file, avih, &chunkdata); + if (avierr != AVIERR_NONE) + goto error; + + /* extract the data */ + file->streams = fetch_32bits(&chunkdata[24]); + + /* allocate memory for the streams */ + file->stream = malloc(sizeof(*file->stream) * file->streams); + if (file->stream == NULL) + goto error; + memset(file->stream, 0, sizeof(*file->stream) * file->streams); + +error: + if (chunkdata != NULL) + free(chunkdata); + return avierr; +} + + +/*------------------------------------------------- + parse_strh_chunk - parse a strh header + chunk +-------------------------------------------------*/ + +static avi_error parse_strh_chunk(avi_file *file, avi_stream *stream, avi_chunk *strh) +{ + UINT8 *chunkdata = NULL; + avi_error avierr; + + /* read the data */ + avierr = read_chunk_data(file, strh, &chunkdata); + if (avierr != AVIERR_NONE) + goto error; + + /* extract the data */ + stream->type = fetch_32bits(&chunkdata[0]); + stream->scale = fetch_32bits(&chunkdata[20]); + stream->rate = fetch_32bits(&chunkdata[24]); + stream->samples = fetch_32bits(&chunkdata[32]); + +error: + if (chunkdata != NULL) + free(chunkdata); + return avierr; +} + + +/*------------------------------------------------- + parse_strf_chunk - parse a strf header + chunk +-------------------------------------------------*/ + +static avi_error parse_strf_chunk(avi_file *file, avi_stream *stream, avi_chunk *strf) +{ + UINT8 *chunkdata = NULL; + avi_error avierr; + + /* read the data */ + avierr = read_chunk_data(file, strf, &chunkdata); + if (avierr != AVIERR_NONE) + goto error; + + /* audio and video streams have differing headers */ + if (stream->type == STREAMTYPE_VIDS) + { + stream->width = fetch_32bits(&chunkdata[4]); + stream->height = fetch_32bits(&chunkdata[8]); + stream->depth = fetch_16bits(&chunkdata[14]); + stream->format = fetch_32bits(&chunkdata[16]); + + /* extra extraction for HuffYUV data */ + if (stream->format == FORMAT_HFYU && strf->size >= 56) + { + avierr = huffyuv_extract_tables(stream, chunkdata, strf->size); + if (avierr != AVIERR_NONE) + goto error; + } + } + else if (stream->type == STREAMTYPE_AUDS) + { + stream->channels = fetch_16bits(&chunkdata[2]); + stream->samplebits = fetch_16bits(&chunkdata[14]); + stream->samplerate = fetch_32bits(&chunkdata[4]); + } + +error: + if (chunkdata != NULL) + free(chunkdata); + return avierr; +} + + +/*------------------------------------------------- + parse_indx_chunk - parse an indx chunk +-------------------------------------------------*/ + +static avi_error parse_indx_chunk(avi_file *file, avi_stream *stream, avi_chunk *strf) +{ + UINT32 entries, entry, id; + UINT8 *chunkdata = NULL; + UINT16 longs_per_entry; + UINT8 subtype, type; + UINT64 baseoffset; + avi_error avierr; + + /* read the data */ + avierr = read_chunk_data(file, strf, &chunkdata); + if (avierr != AVIERR_NONE) + goto error; + + /* extract the data */ + longs_per_entry = fetch_16bits(&chunkdata[0]); + subtype = chunkdata[2]; + type = chunkdata[3]; + entries = fetch_32bits(&chunkdata[4]); + id = fetch_32bits(&chunkdata[8]); + baseoffset = fetch_64bits(&chunkdata[12]); + + /* if this is a superindex, loop over entries and call ourselves recursively */ + if (type == AVI_INDEX_OF_INDEXES) + { + /* validate the size of each entry */ + if (longs_per_entry != 4) + return AVIERR_INVALID_DATA; + + /* loop over entries and create subchunks for each */ + for (entry = 0; entry < entries; entry++) + { + const UINT8 *base = &chunkdata[24 + entry * 16]; + file_error filerr; + avi_chunk subchunk; + UINT32 bytes_read; + UINT8 buffer[8]; + + /* go read the subchunk */ + subchunk.offset = fetch_64bits(&base[0]); + filerr = osd_read(file->file, buffer, subchunk.offset, sizeof(buffer), &bytes_read); + if (filerr != FILERR_NONE || bytes_read != sizeof(buffer)) + { + avierr = AVIERR_READ_ERROR; + break; + } + + /* parse the data */ + subchunk.type = fetch_32bits(&buffer[0]); + subchunk.size = fetch_32bits(&buffer[4]); + + /* recursively parse each referenced chunk; stop if we hit an error */ + avierr = parse_indx_chunk(file, stream, &subchunk); + if (avierr != AVIERR_NONE) + break; + } + } + + /* otherwise, this is a standard index */ + else if (type == AVI_INDEX_OF_CHUNKS) + { + /* validate the size of each entry */ + if (longs_per_entry != 2 && longs_per_entry != 3) + return AVIERR_INVALID_DATA; + + /* loop over entries and parse out the data */ + for (entry = 0; entry < entries; entry++) + { + const UINT8 *base = &chunkdata[24 + entry * 4 * longs_per_entry]; + UINT32 offset = fetch_32bits(&base[0]); + UINT32 size = fetch_32bits(&base[4]); + + /* set the info for this chunk and advance */ + avierr = set_stream_chunk_info(stream, stream->chunks++, baseoffset + offset - 8, size + 8); + if (avierr != AVIERR_NONE) + break; + } + } + +error: + if (chunkdata != NULL) + free(chunkdata); + return avierr; +} + + +/*------------------------------------------------- + parse_idx1_chunk - parse an idx1 chunk +-------------------------------------------------*/ + +static avi_error parse_idx1_chunk(avi_file *file, UINT64 baseoffset, avi_chunk *idx1) +{ + UINT8 *chunkdata = NULL; + avi_error avierr; + UINT32 entries; + UINT32 entry; + + /* read the data */ + avierr = read_chunk_data(file, idx1, &chunkdata); + if (avierr != AVIERR_NONE) + goto error; + + /* loop over entries */ + entries = idx1->size / 16; + for (entry = 0; entry < entries; entry++) + { + const UINT8 *base = &chunkdata[entry * 16]; + UINT32 chunkid = fetch_32bits(&base[0]); + UINT32 offset = fetch_32bits(&base[8]); + UINT32 size = fetch_32bits(&base[12]); + avi_stream *stream; + int streamnum; + + /* determine the stream index */ + streamnum = ((chunkid >> 8) & 0xff) - '0'; + streamnum += 10 * ((chunkid & 0xff) - '0'); + if (streamnum >= file->streams) + { + avierr = AVIERR_INVALID_DATA; + goto error; + } + stream = &file->stream[streamnum]; + + /* set the appropriate entry */ + avierr = set_stream_chunk_info(stream, stream->chunks++, baseoffset + offset, size + 8); + if (avierr != AVIERR_NONE) + goto error; + } + +error: + if (chunkdata != NULL) + free(chunkdata); + return avierr; +} + + +/*------------------------------------------------- + chunk_open - open a new chunk for writing +-------------------------------------------------*/ + +static avi_error chunk_open(avi_file *file, UINT32 type, UINT32 listtype, UINT32 estlength) +{ + file_error filerr; + avi_chunk *chunk; + UINT32 written; + + /* if we're out of stack entries, bail */ + if (file->chunksp >= ARRAY_LENGTH(file->chunkstack)) + return AVIERR_STACK_TOO_DEEP; + chunk = &file->chunkstack[file->chunksp++]; + + /* set up the chunk information */ + chunk->offset = file->writeoffs; + chunk->size = estlength; + chunk->type = type; + chunk->listtype = listtype; + + /* non-list types */ + if (type != CHUNKTYPE_RIFF && type != CHUNKTYPE_LIST) + { + UINT8 buffer[8]; + + /* populate the header */ + put_32bits(&buffer[0], chunk->type); + put_32bits(&buffer[4], chunk->size); + + /* write the header */ + filerr = osd_write(file->file, buffer, file->writeoffs, sizeof(buffer), &written); + if (filerr != FILERR_NONE || written != sizeof(buffer)) + return AVIERR_WRITE_ERROR; + file->writeoffs += written; + } + + /* list types */ + else + { + UINT8 buffer[12]; + + /* populate the header */ + put_32bits(&buffer[0], chunk->type); + put_32bits(&buffer[4], chunk->size); + put_32bits(&buffer[8], chunk->listtype); + + /* write the header */ + filerr = osd_write(file->file, buffer, file->writeoffs, sizeof(buffer), &written); + if (filerr != FILERR_NONE || written != sizeof(buffer)) + return AVIERR_WRITE_ERROR; + file->writeoffs += written; + } + + return AVIERR_NONE; +} + + +/*------------------------------------------------- + chunk_close - finish writing an chunk +-------------------------------------------------*/ + +static avi_error chunk_close(avi_file *file) +{ + avi_chunk *chunk = &file->chunkstack[--file->chunksp]; + UINT64 chunksize = file->writeoffs - (chunk->offset + 8); + UINT32 written; + + /* error if we don't fit into 32 bits */ + if (chunksize != (UINT32)chunksize) + return AVIERR_INVALID_DATA; + + /* write the final size if it is different from the guess */ + if (chunk->size != chunksize) + { + file_error filerr; + UINT8 buffer[4]; + + put_32bits(&buffer[0], (UINT32)chunksize); + filerr = osd_write(file->file, buffer, chunk->offset + 4, sizeof(buffer), &written); + if (filerr != FILERR_NONE || written != sizeof(buffer)) + return AVIERR_WRITE_ERROR; + } + + /* round up to the next word */ + file->writeoffs += chunksize & 1; + + return AVIERR_NONE; +} + + +/*------------------------------------------------- + chunk_write - write an chunk and its data +-------------------------------------------------*/ + +static avi_error chunk_write(avi_file *file, UINT32 type, const void *data, UINT32 length) +{ + file_error filerr; + avi_error avierr; + UINT32 written; + + /* if we are getting too big, split the RIFF */ + if ((file->riffbase == 0 && file->writeoffs + length + compute_idx1_size(file) >= MAX_RIFF_SIZE) || + file->writeoffs + length - file->riffbase >= MAX_RIFF_SIZE) + { + /* close the movi list */ + avierr = chunk_close(file); + if (avierr != AVIERR_NONE) + return avierr; + + /* write the idx1 chunk if this is the first */ + if (file->riffbase == 0) + { + avierr = write_idx1_chunk(file); + if (avierr != AVIERR_NONE) + return avierr; + } + + /* close the RIFF */ + avierr = chunk_close(file); + if (avierr != AVIERR_NONE) + return avierr; + + /* open a new RIFF */ + file->riffbase = file->writeoffs; + avierr = chunk_open(file, CHUNKTYPE_RIFF, LISTTYPE_AVIX, 0); + if (avierr != AVIERR_NONE) + return avierr; + + /* open a nested movi list */ + file->saved_movi_offset = file->writeoffs; + avierr = chunk_open(file, CHUNKTYPE_LIST, LISTTYPE_MOVI, 0); + if (avierr != AVIERR_NONE) + return avierr; + } + + /* open the chunk */ + avierr = chunk_open(file, type, 0, length); + if (avierr != AVIERR_NONE) + return avierr; + + /* write the data */ + filerr = osd_write(file->file, data, file->writeoffs, length, &written); + if (filerr != FILERR_NONE || written != length) + return AVIERR_WRITE_ERROR; + file->writeoffs += written; + + /* close the chunk */ + return chunk_close(file); +} + + +/*------------------------------------------------- + chunk_overwrite - write a chunk in two passes; + first pass writes to the end of file and + saves the offset; second pass overwrites the + original +-------------------------------------------------*/ + +static avi_error chunk_overwrite(avi_file *file, UINT32 type, const void *data, UINT32 length, UINT64 *offset, int initial_write) +{ + UINT64 savedoffset = 0; + avi_error avierr; + + /* if this is our initial write, save the offset */ + if (initial_write) + *offset = file->writeoffs; + + /* otherwise, remember the current write offset and replace it with the original */ + else + { + savedoffset = file->writeoffs; + file->writeoffs = *offset; + } + + /* write the chunk */ + avierr = chunk_write(file, type, data, length); + + /* if this isn't the initial write, restore the previous offset */ + if (!initial_write) + file->writeoffs = savedoffset; + + return avierr; +} + + +/*------------------------------------------------- + write_initial_headers - write out the inital + set of AVI and stream headers +-------------------------------------------------*/ + +static avi_error write_initial_headers(avi_file *file) +{ + avi_error avierr; + int strnum; + + /* reset the write pointer */ + file->writeoffs = 0; + + /* open a RIFF chunk */ + avierr = chunk_open(file, CHUNKTYPE_RIFF, LISTTYPE_AVI, 0); + if (avierr != AVIERR_NONE) + return avierr; + + /* open a hdlr LIST */ + avierr = chunk_open(file, CHUNKTYPE_LIST, LISTTYPE_HDRL, 0); + if (avierr != AVIERR_NONE) + return avierr; + + /* write an avih chunk */ + avierr = write_avih_chunk(file, TRUE); + if (avierr != AVIERR_NONE) + return avierr; + + /* for each stream, write a strl LIST */ + for (strnum = 0; strnum < file->streams; strnum++) + { + /* open a strl LIST */ + avierr = chunk_open(file, CHUNKTYPE_LIST, LISTTYPE_STRL, 0); + if (avierr != AVIERR_NONE) + return avierr; + + /* write the strh chunk */ + avierr = write_strh_chunk(file, &file->stream[strnum], TRUE); + if (avierr != AVIERR_NONE) + return avierr; + + /* write the strf chunk */ + avierr = write_strf_chunk(file, &file->stream[strnum]); + if (avierr != AVIERR_NONE) + return avierr; + + /* write the indx chunk */ + avierr = write_indx_chunk(file, &file->stream[strnum], TRUE); + if (avierr != AVIERR_NONE) + return avierr; + + /* close the strl LIST */ + avierr = chunk_close(file); + if (avierr != AVIERR_NONE) + return avierr; + } + + /* close the hdlr LIST */ + avierr = chunk_close(file); + if (avierr != AVIERR_NONE) + return avierr; + + /* open a movi LIST */ + file->saved_movi_offset = file->writeoffs; + avierr = chunk_open(file, CHUNKTYPE_LIST, LISTTYPE_MOVI, 0); + if (avierr != AVIERR_NONE) + return avierr; + + return avierr; +} + + +/*------------------------------------------------- + write_avih_chunk - write the avih header + chunk +-------------------------------------------------*/ + +static avi_error write_avih_chunk(avi_file *file, int initial_write) +{ + avi_stream *video = get_video_stream(file); + UINT8 buffer[56]; + + /* reset the buffer */ + memset(buffer, 0, sizeof(buffer)); + + put_32bits(&buffer[0], 1000000 * (INT64)video->scale / video->rate); /* dwMicroSecPerFrame */ + put_32bits(&buffer[12], AVIF_HASINDEX | AVIF_ISINTERLEAVED); /* dwFlags */ + put_32bits(&buffer[16], video->samples); /* dwTotalFrames */ + put_32bits(&buffer[24], file->streams); /* dwStreams */ + put_32bits(&buffer[32], video->width); /* dwWidth */ + put_32bits(&buffer[36], video->height); /* dwHeight */ + + /* (over)write the chunk */ + return chunk_overwrite(file, CHUNKTYPE_AVIH, buffer, sizeof(buffer), &file->saved_avih_offset, initial_write); +} + + +/*------------------------------------------------- + write_strh_chunk - write the strh header + chunk +-------------------------------------------------*/ + +static avi_error write_strh_chunk(avi_file *file, avi_stream *stream, int initial_write) +{ + UINT8 buffer[56]; + + /* reset the buffer */ + memset(buffer, 0, sizeof(buffer)); + + put_32bits(&buffer[0], stream->type); /* fccType */ + put_32bits(&buffer[20], stream->scale); /* dwScale */ + put_32bits(&buffer[24], stream->rate); /* dwRate */ + put_32bits(&buffer[32], stream->samples); /* dwLength */ + put_32bits(&buffer[40], 10000); /* dwQuality */ + + /* video-stream specific data */ + if (stream->type == STREAMTYPE_VIDS) + { + put_32bits(&buffer[4], /* fccHandler */ + (stream->format == FORMAT_HFYU) ? HANDLER_HFYU : HANDLER_DIB); + put_32bits(&buffer[36], /* dwSuggestedBufferSize */ + stream->width * stream->height * 4); + put_16bits(&buffer[52], stream->width); /* rcFrame.right */ + put_16bits(&buffer[54], stream->height); /* rcFrame.bottom */ + } + + /* audio-stream specific data */ + if (stream->type == STREAMTYPE_AUDS) + { + put_32bits(&buffer[36], /* dwSuggestedBufferSize */ + stream->samplerate * stream->channels * (stream->samplebits / 8)); + put_32bits(&buffer[44], /* dwSampleSize */ + stream->channels * (stream->samplebits / 8)); + } + + /* write the chunk */ + return chunk_overwrite(file, CHUNKTYPE_STRH, buffer, sizeof(buffer), &stream->saved_strh_offset, initial_write); +} + + +/*------------------------------------------------- + write_strf_chunk - write the strf header + chunk +-------------------------------------------------*/ + +static avi_error write_strf_chunk(avi_file *file, avi_stream *stream) +{ + /* video stream */ + if (stream->type == STREAMTYPE_VIDS) + { + UINT8 buffer[40]; + + /* reset the buffer */ + memset(buffer, 0, sizeof(buffer)); + + put_32bits(&buffer[0], sizeof(buffer)); /* biSize */ + put_32bits(&buffer[4], stream->width); /* biWidth */ + put_32bits(&buffer[8], stream->height); /* biHeight */ + put_16bits(&buffer[12], 1); /* biPlanes */ + put_16bits(&buffer[14], stream->depth); /* biBitCount */ + put_32bits(&buffer[16], stream->format); /* biCompression */ + put_32bits(&buffer[20], /* biSizeImage */ + stream->width * stream->height * (stream->depth + 7) / 8); + + /* write the chunk */ + return chunk_write(file, CHUNKTYPE_STRF, buffer, sizeof(buffer)); + } + + /* audio stream */ + if (stream->type == STREAMTYPE_AUDS) + { + UINT8 buffer[16]; + + /* reset the buffer */ + memset(buffer, 0, sizeof(buffer)); + + put_16bits(&buffer[0], 1); /* wFormatTag */ + put_16bits(&buffer[2], stream->channels); /* nChannels */ + put_32bits(&buffer[4], stream->samplerate); /* nSamplesPerSec */ + put_32bits(&buffer[8], /* nAvgBytesPerSec */ + stream->samplerate * stream->channels * (stream->samplebits / 8)); + put_16bits(&buffer[12], /* nBlockAlign */ + stream->channels * (stream->samplebits / 8)); + put_16bits(&buffer[14], stream->samplebits); /* wBitsPerSample */ + + /* write the chunk */ + return chunk_write(file, CHUNKTYPE_STRF, buffer, sizeof(buffer)); + } + + return AVIERR_INVALID_DATA; +} + + +/*------------------------------------------------- + write_indx_chunk - write the indx header + chunk +-------------------------------------------------*/ + +static avi_error write_indx_chunk(avi_file *file, avi_stream *stream, int initial_write) +{ + UINT8 buffer[24 + 16 * MAX_AVI_SIZE_IN_GB / 4]; + UINT32 chunkid, indexchunkid; + UINT32 master_entries = 0; + + /* reset the buffer */ + memset(buffer, 0, sizeof(buffer)); + + /* construct the chunk ID and index chunk ID */ + chunkid = get_chunkid_for_stream(file, stream); + indexchunkid = AVI_FOURCC('i', 'x', '0' + (stream - file->stream) / 10, '0' + (stream - file->stream) % 10); + + /* loop over chunks of 4GB and write out indexes for them first */ + if (!initial_write && file->riffbase != 0) + { + UINT64 currentbase; + for (currentbase = 0; currentbase < file->writeoffs; currentbase += FOUR_GB) + { + UINT64 currentend = currentbase + FOUR_GB; + UINT32 chunks_this_index = 0; + UINT32 bytes_this_index = 0; + avi_error avierr; + UINT32 chunknum; + UINT8 *tempbuf; + + /* count chunks that are in this region */ + for (chunknum = 0; chunknum < stream->chunks; chunknum++) + if (stream->chunk[chunknum].offset >= currentbase && stream->chunk[chunknum].offset < currentend) + chunks_this_index++; + + /* if no chunks, skip */ + if (chunks_this_index == 0) + continue; + + /* allocate memory */ + tempbuf = malloc(24 + 8 * chunks_this_index); + if (tempbuf == NULL) + return AVIERR_NO_MEMORY; + memset(tempbuf, 0, 24 + 8 * chunks_this_index); + + /* make a regular index */ + put_16bits(&tempbuf[0], 2); /* wLongsPerEntry */ + tempbuf[2] = 0; /* bIndexSubType */ + tempbuf[3] = AVI_INDEX_OF_CHUNKS; /* bIndexType */ + put_32bits(&tempbuf[4], chunks_this_index); /* nEntriesInUse */ + put_32bits(&tempbuf[8], chunkid); /* dwChunkId */ + put_64bits(&tempbuf[12], currentbase); /* qwBaseOffset */ + + /* now fill in the indexes */ + chunks_this_index = 0; + for (chunknum = 0; chunknum < stream->chunks; chunknum++) + if (stream->chunk[chunknum].offset >= currentbase && stream->chunk[chunknum].offset < currentend) + { + put_32bits(&tempbuf[24 + 8 * chunks_this_index + 0], stream->chunk[chunknum].offset + 8 - currentbase); + put_32bits(&tempbuf[24 + 8 * chunks_this_index + 4], stream->chunk[chunknum].length - 8); + bytes_this_index += stream->chunk[chunknum].length; + chunks_this_index++; + } + + /* write the offset of this index to the master table */ + put_64bits(&buffer[24 + 16 * master_entries + 0], file->writeoffs); + put_32bits(&buffer[24 + 16 * master_entries + 8], 24 + 8 * chunks_this_index + 8); + if (stream->type == STREAMTYPE_VIDS) + put_32bits(&buffer[24 + 16 * master_entries + 12], chunks_this_index); + else if (stream->type == STREAMTYPE_AUDS) + put_32bits(&buffer[24 + 16 * master_entries + 12], bytes_this_index / ((stream->samplebits / 8) * stream->channels)); + master_entries++; + + /* write the index */ + avierr = chunk_write(file, indexchunkid, tempbuf, 24 + 8 * chunks_this_index); + free(tempbuf); + if (avierr != AVIERR_NONE) + return avierr; + } + } + + /* build up the master index */ + if (master_entries != 0) + { + put_16bits(&buffer[0], 4); /* wLongsPerEntry */ + buffer[2] = 0; /* bIndexSubType */ + buffer[3] = AVI_INDEX_OF_INDEXES; /* bIndexType */ + put_32bits(&buffer[4], master_entries); /* nEntriesInUse */ + put_32bits(&buffer[8], chunkid); /* dwChunkId */ + } + + /* (over)write the chunk */ + return chunk_overwrite(file, (master_entries == 0) ? CHUNKTYPE_JUNK : CHUNKTYPE_INDX, buffer, sizeof(buffer), &stream->saved_indx_offset, initial_write); +} + + +/*------------------------------------------------- + write_idx1_chunk - write the idx1 chunk +-------------------------------------------------*/ + +static avi_error write_idx1_chunk(avi_file *file) +{ + UINT32 tempbuflength = compute_idx1_size(file) - 8; + UINT32 curchunk[2] = { 0 }; + UINT32 curoffset; + avi_error avierr; + UINT8 *tempbuf; + + /* allocate a temporary buffer */ + tempbuf = malloc(tempbuflength); + if (tempbuf == NULL) + return AVIERR_NO_MEMORY; + + /* fill it in */ + for (curoffset = 0; curoffset < tempbuflength; curoffset += 16) + { + UINT64 minoffset = ~(UINT64)0; + int strnum, minstr = 0; + + /* determine which stream has the next chunk */ + for (strnum = 0; strnum < file->streams; strnum++) + if (curchunk[strnum] < file->stream[strnum].chunks && file->stream[strnum].chunk[curchunk[strnum]].offset < minoffset) + { + minoffset = file->stream[strnum].chunk[curchunk[strnum]].offset; + minstr = strnum; + } + + /* make an entry for this index */ + put_32bits(&tempbuf[curoffset + 0], get_chunkid_for_stream(file, &file->stream[minstr])); + put_32bits(&tempbuf[curoffset + 4], 0x0010 /* AVIIF_KEYFRAME */); + put_32bits(&tempbuf[curoffset + 8], minoffset - (file->saved_movi_offset + 8)); + put_32bits(&tempbuf[curoffset + 12], file->stream[minstr].chunk[curchunk[minstr]].length - 8); + + /* advance the chunk counter for this stream */ + curchunk[minstr]++; + } + + /* write the chunk */ + avierr = chunk_write(file, CHUNKTYPE_IDX1, tempbuf, tempbuflength); + free(tempbuf); + return avierr; +} + + +/*------------------------------------------------- + soundbuf_initialize - initialize the sound + buffering system +-------------------------------------------------*/ + +static avi_error soundbuf_initialize(avi_file *file) +{ + avi_stream *audio = get_audio_stream(file, 0, NULL); + avi_stream *video = get_video_stream(file); + + /* we require a video stream */ + if (video == NULL) + return AVIERR_UNSUPPORTED_VIDEO_FORMAT; + + /* skip if no audio stream */ + if (audio == NULL) + return AVIERR_NONE; + + /* determine the number of samples we want in our buffer; 2 seconds should be enough */ + file->soundbuf_samples = file->info.audio_samplerate * SOUND_BUFFER_MSEC / 1000; + + /* allocate a buffer */ + file->soundbuf = malloc(file->soundbuf_samples * file->info.audio_channels * sizeof(file->soundbuf[0])); + if (file->soundbuf == NULL) + return AVIERR_NO_MEMORY; + memset(file->soundbuf, 0, file->soundbuf_samples * file->info.audio_channels * sizeof(file->soundbuf[0])); + + /* determine the number of frames to be ahead (0.75secs) */ + file->soundbuf_frames = ((UINT64)video->rate * 75) / ((UINT64)video->scale * 100) + 1; + return AVIERR_NONE; +} + + +/*------------------------------------------------- + soundbuf_write_chunk - write the initial + chunk data +-------------------------------------------------*/ + +static avi_error soundbuf_write_chunk(avi_file *file, UINT32 framenum) +{ + avi_stream *stream = get_audio_stream(file, 0, NULL); + avi_error avierr; + UINT32 chunknum; + UINT32 length; + + /* skip if no audio stream */ + if (stream == NULL) + return AVIERR_NONE; + + /* determine the length of this chunk */ + if (framenum == 0) + length = framenum_to_samplenum(file, file->soundbuf_frames); + else + length = framenum_to_samplenum(file, framenum + 1 + file->soundbuf_frames) - framenum_to_samplenum(file, framenum + file->soundbuf_frames); + length *= stream->channels * sizeof(INT16); + + /* set the info for this new chunk */ + chunknum = stream->chunks; + avierr = set_stream_chunk_info(stream, chunknum, file->writeoffs, length + 8); + if (avierr != AVIERR_NONE) + return avierr; + + /* then do the initial write */ + return chunk_overwrite(file, get_chunkid_for_stream(file, stream), file->soundbuf, length, &stream->chunk[chunknum].offset, TRUE); +} + + +/*------------------------------------------------- + soundbuf_flush - flush data from the sound + buffers +-------------------------------------------------*/ + +static avi_error soundbuf_flush(avi_file *file, int only_flush_full) +{ + avi_stream *stream = get_audio_stream(file, 0, NULL); + INT32 channelsamples = file->soundbuf_samples; + INT32 processedsamples = 0; + UINT32 bytes_per_sample; + UINT32 finalchunks; + avi_error avierr; + UINT32 chunknum; + UINT32 chunkid; + int channel; + + /* skip if no stream */ + if (stream == NULL) + return AVIERR_NONE; + + /* get the chunk ID for this stream */ + chunkid = get_chunkid_for_stream(file, stream); + bytes_per_sample = stream->channels * sizeof(INT16); + finalchunks = stream->chunks; + + /* find out how many samples we've accumulated */ + for (channel = 0; channel < stream->channels; channel++) + channelsamples = MIN(channelsamples, file->soundbuf_chansamples[channel]); + + /* loop over pending sound chunks */ + for (chunknum = file->soundbuf_chunks; chunknum < stream->chunks; chunknum++) + { + avi_chunk_list *chunk = &stream->chunk[chunknum]; + UINT32 chunksamples = (chunk->length - 8) / bytes_per_sample; + + /* stop if we don't have enough to satisfy this chunk */ + if (only_flush_full && channelsamples < chunksamples) + break; + + /* if we don't have all the samples we need, pad with 0's */ + if (channelsamples > 0 && channelsamples < chunksamples) + { + if (processedsamples + chunksamples > file->soundbuf_samples) + return AVIERR_EXCEEDED_SOUND_BUFFER; + memset(&file->soundbuf[(processedsamples + channelsamples) * stream->channels], 0, (chunksamples - channelsamples) * bytes_per_sample); + } + + /* if we're completely out of samples, clear the buffer entirely and use the end */ + else if (channelsamples <= 0) + { + processedsamples = file->soundbuf_samples - chunksamples; + memset(&file->soundbuf[processedsamples * stream->channels], 0, chunksamples * bytes_per_sample); + chunkid = CHUNKTYPE_JUNK; + finalchunks--; + } + + /* copy the sample data in */ + avierr = chunk_overwrite(file, chunkid, &file->soundbuf[processedsamples * stream->channels], chunk->length - 8, &chunk->offset, FALSE); + if (avierr != AVIERR_NONE) + return avierr; + + /* add up the samples */ + if (channelsamples > 0) + file->info.audio_numsamples = stream->samples += channelsamples; + + /* advance past those */ + processedsamples += chunksamples; + channelsamples -= chunksamples; + channelsamples = MAX(0, channelsamples); + } + + /* if we have a non-zero offset, shift everything down */ + if (processedsamples > 0) + { + /* first account for the samples we processed */ + memmove(&file->soundbuf[0], &file->soundbuf[processedsamples * stream->channels], (file->soundbuf_samples - processedsamples) * bytes_per_sample); + for (channel = 0; channel < stream->channels; channel++) + file->soundbuf_chansamples[channel] -= processedsamples; + } + + /* update the final chunk count */ + if (!only_flush_full) + stream->chunks = finalchunks; + + /* account for flushed chunks */ + file->soundbuf_chunks = chunknum; + return AVIERR_NONE; +} + + +/*------------------------------------------------- + yuv_decompress_to_yuy16 - decompress a YUV + encoded frame to a YUY16 bitmap +-------------------------------------------------*/ + +static avi_error yuv_decompress_to_yuy16(avi_stream *stream, const UINT8 *data, UINT32 numbytes, bitmap_t *bitmap) +{ + const UINT16 *dataend = (const UINT16 *)(data + numbytes); + int x, y; + + /* compressed video */ + for (y = 0; y < stream->height; y++) + { + const UINT16 *source = (const UINT16 *)data + y * stream->width; + UINT16 *dest = (UINT16 *)bitmap->base + y * bitmap->rowpixels; + + /* switch off the compression */ + switch (stream->format) + { + case FORMAT_UYVY: + for (x = 0; x < stream->width && source < dataend; x++) + *dest++ = *source++; + break; + + case FORMAT_VYUY: + case FORMAT_YUY2: + for (x = 0; x < stream->width && source < dataend; x++) + { + UINT16 pix = *source++; + *dest++ = (pix >> 8) | (pix << 8); + } + break; + } + } + + return AVIERR_NONE; +} + + +/*------------------------------------------------- + yuy16_compress_to_yuy - "compress" a YUY16 + bitmap to a YUV encoded frame +-------------------------------------------------*/ + +static avi_error yuy16_compress_to_yuy(avi_stream *stream, const bitmap_t *bitmap, UINT8 *data, UINT32 numbytes) +{ + const UINT16 *dataend = (const UINT16 *)(data + numbytes); + int x, y; + + /* compressed video */ + for (y = 0; y < stream->height; y++) + { + const UINT16 *source = (UINT16 *)bitmap->base + y * bitmap->rowpixels; + UINT16 *dest = (UINT16 *)data + y * stream->width; + + /* switch off the compression */ + switch (stream->format) + { + case FORMAT_UYVY: + for (x = 0; x < stream->width && dest < dataend; x++) + *dest++ = *source++; + break; + + case FORMAT_VYUY: + case FORMAT_YUY2: + for (x = 0; x < stream->width && source < dataend; x++) + { + UINT16 pix = *source++; + *dest++ = (pix >> 8) | (pix << 8); + } + break; + } + } + + return AVIERR_NONE; +} + + +/*------------------------------------------------- + huffyuv_extract_tables - extract HuffYUV + tables +-------------------------------------------------*/ + +static avi_error huffyuv_extract_tables(avi_stream *stream, const UINT8 *chunkdata, UINT32 size) +{ + const UINT8 *chunkend = chunkdata + size; + avi_error avierr = AVIERR_NONE; + int tabnum; + + /* allocate memory for the data */ + stream->huffyuv = malloc(sizeof(*stream->huffyuv)); + if (stream->huffyuv == NULL) + { + avierr = AVIERR_NO_MEMORY; + goto error; + } + + /* extract predictor information */ + if (&chunkdata[40] >= chunkend) + return AVIERR_INVALID_DATA; + stream->huffyuv->predictor = chunkdata[40]; + + /* make sure it's the left predictor */ + if ((stream->huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) != HUFFYUV_PREDICT_LEFT) + return AVIERR_UNSUPPORTED_VIDEO_FORMAT; + + /* make sure it's 16bpp YUV data */ + if (chunkdata[41] != 16) + return AVIERR_UNSUPPORTED_VIDEO_FORMAT; + chunkdata += 44; + + /* loop over tables */ + for (tabnum = 0; tabnum < 3; tabnum++) + { + huffyuv_table *table = &stream->huffyuv->table[tabnum]; + UINT32 curbits, bitadd; + UINT16 bitsat16 = 0; + int offset = 0, bits; + + /* loop until we populate the whole table */ + while (offset < 256) + { + int data, shift, count, i; + + /* extract the next run */ + if (chunkdata >= chunkend) + { + avierr = AVIERR_INVALID_DATA; + goto error; + } + data = *chunkdata++; + shift = data & 0x1f; + count = data >> 5; + + /* zero count means next whole byte is a count */ + if (count == 0) + { + if (chunkdata >= chunkend) + { + avierr = AVIERR_INVALID_DATA; + goto error; + } + count = *chunkdata++; + } + for (i = 0; i < count; i++) + table->shift[offset++] = shift; + } + + /* now determine bit patterns and masks */ + curbits = 0; + for (bits = 31; bits >= 0; bits--) + { + bitadd = 1 << (32 - bits); + + /* make sure we've cleared out all the bits below */ + if ((curbits & (bitadd - 1)) != 0) + { + avierr = AVIERR_INVALID_DATA; + goto error; + } + + /* find all entries with this shift count and assign them */ + for (offset = 0; offset < 256; offset++) + if (table->shift[offset] == bits) + { + table->bits[offset] = curbits; + table->mask[offset] = ~(bitadd - 1); + curbits += bitadd; + } + + /* remember the bit pattern when we complete all the 17-bit codes */ + if (bits == 17) + bitsat16 = curbits >> 16; + } + + /* allocate the number of extra lookup tables we need */ + if (bitsat16 > 0) + { + table->extralookup = malloc(bitsat16 * 65536 * sizeof(table->extralookup[0])); + if (table->extralookup == NULL) + { + avierr = AVIERR_NO_MEMORY; + goto error; + } + for (offset = 0; offset < bitsat16; offset++) + table->baselookup[offset] = (offset << 8) | 0; + } + + /* then create lookup tables */ + for (offset = 0; offset < 256; offset++) + if (table->shift[offset] > 16) + { + UINT16 *tablebase = table->extralookup + (table->bits[offset] >> 16) * 65536; + UINT32 start = table->bits[offset] & 0xffff; + UINT32 end = start + ((1 << (32 - table->shift[offset])) - 1); + while (start <= end) + tablebase[start++] = (offset << 8) | (table->shift[offset] - 16); + } + else if (table->shift[offset] > 0) + { + UINT32 start = table->bits[offset] >> 16; + UINT32 end = start + ((1 << (16 - table->shift[offset])) - 1); + while (start <= end) + table->baselookup[start++] = (offset << 8) | table->shift[offset]; + } + } + +error: + if (avierr != AVIERR_NONE && stream->huffyuv != NULL) + { + free(stream->huffyuv); + stream->huffyuv = NULL; + } + return avierr; +} + + +/*------------------------------------------------- + huffyuv_decompress_to_yuy16 - decompress a + HuffYUV-encoded frame to a YUY16 bitmap +-------------------------------------------------*/ + +static avi_error huffyuv_decompress_to_yuy16(avi_stream *stream, const UINT8 *data, UINT32 numbytes, bitmap_t *bitmap) +{ + huffyuv_data *huffyuv = stream->huffyuv; + int prevlines = (stream->height > 288) ? 2 : 1; + UINT8 lastprevy = 0, lastprevcb = 0, lastprevcr = 0; + UINT8 lasty = 0, lastcb = 0, lastcr = 0; + UINT8 bitsinbuffer = 0; + UINT32 bitbuffer = 0; + UINT32 dataoffs = 0; + int x, y; + + /* compressed video */ + for (y = 0; y < stream->height; y++) + { + UINT16 *dest = (UINT16 *)bitmap->base + y * bitmap->rowpixels; + + /* handle the first four bytes independently */ + x = 0; + if (y == 0) + { + /* first DWORD is stored as YUY2 */ + lasty = data[dataoffs++]; + lastcb = data[dataoffs++]; + *dest++ = (lasty << 8) | lastcb; + lasty = data[dataoffs++]; + lastcr = data[dataoffs++]; + *dest++ = (lasty << 8) | lastcr; + x = 2; + } + + /* loop over pixels */ + for ( ; x < stream->width; x++) + { + huffyuv_table *ytable = &huffyuv->table[0]; + huffyuv_table *ctable = &huffyuv->table[1 + (x & 1)]; + UINT16 pixel, huffdata; + int shift; + + /* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */ + while (bitsinbuffer <= 24 && dataoffs < numbytes) + { + bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer); + bitsinbuffer += 8; + } + + /* look up the Y component */ + huffdata = ytable->baselookup[bitbuffer >> 16]; + shift = huffdata & 0xff; + if (shift == 0) + { + bitsinbuffer -= 16; + bitbuffer <<= 16; + + /* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */ + while (bitsinbuffer <= 24 && dataoffs < numbytes) + { + bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer); + bitsinbuffer += 8; + } + + huffdata = ytable->extralookup[(huffdata >> 8) * 65536 + (bitbuffer >> 16)]; + shift = huffdata & 0xff; + } + bitsinbuffer -= shift; + bitbuffer <<= shift; + pixel = huffdata & 0xff00; + + /* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */ + while (bitsinbuffer <= 24 && dataoffs < numbytes) + { + bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer); + bitsinbuffer += 8; + } + + /* look up the Cb/Cr component */ + huffdata = ctable->baselookup[bitbuffer >> 16]; + shift = huffdata & 0xff; + if (shift == 0) + { + bitsinbuffer -= 16; + bitbuffer <<= 16; + + /* fill up the buffer; they store little-endian DWORDs, so we XOR with 3 */ + while (bitsinbuffer <= 24 && dataoffs < numbytes) + { + bitbuffer |= data[dataoffs++ ^ 3] << (24 - bitsinbuffer); + bitsinbuffer += 8; + } + + huffdata = ctable->extralookup[(huffdata >> 8) * 65536 + (bitbuffer >> 16)]; + shift = huffdata & 0xff; + } + bitsinbuffer -= shift; + bitbuffer <<= shift; + *dest++ = pixel | (huffdata >> 8); + } + } + + /* apply deltas */ + lasty = lastcb = lastcr = 0; + lastprevy = lastprevcb = lastprevcr = 0; + for (y = 0; y < stream->height; y++) + { + UINT16 *dest = (UINT16 *)bitmap->base + y * bitmap->rowpixels; + UINT16 *prevrow = dest - prevlines * bitmap->rowpixels; + + /* left predict or gradient predict */ + if ((huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_LEFT || + ((huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_GRADIENT)) + { + /* first do left deltas */ + for (x = 0; x < stream->width; x += 2) + { + UINT16 pixel0 = dest[x + 0]; + UINT16 pixel1 = dest[x + 1]; + + lasty += pixel0 >> 8; + lastcb += pixel0; + dest[x + 0] = (lasty << 8) | lastcb; + + lasty += pixel1 >> 8; + lastcr += pixel1; + dest[x + 1] = (lasty << 8) | lastcr; + } + + /* for gradient, we then add in the previous row */ + if ((huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_GRADIENT && y >= prevlines) + for (x = 0; x < stream->width; x++) + { + UINT16 curpix = dest[x]; + UINT16 prevpix = prevrow[x]; + UINT8 ysum = (curpix >> 8) + (prevpix >> 8); + UINT8 csum = curpix + prevpix; + dest[x] = (ysum << 8) | csum; + } + } + + /* median predict on rows > 0 */ + else if ((huffyuv->predictor & ~HUFFYUV_PREDICT_DECORR) == HUFFYUV_PREDICT_MEDIAN && y >= prevlines) + { + for (x = 0; x < stream->width; x += 2) + { + UINT16 prevpixel0 = prevrow[x + 0]; + UINT16 prevpixel1 = prevrow[x + 1]; + UINT16 pixel0 = dest[x + 0]; + UINT16 pixel1 = dest[x + 1]; + UINT8 a, b, c; + + /* compute previous, above, and (prev + above - above-left) */ + a = lasty; + b = prevpixel0 >> 8; + c = lastprevy; + lastprevy = b; + if (a > b) { UINT8 tmp = a; a = b; b = tmp; } + if (a > c) { UINT8 tmp = a; a = c; c = tmp; } + if (b > c) { UINT8 tmp = b; b = c; c = tmp; } + lasty = (pixel0 >> 8) + b; + + /* compute previous, above, and (prev + above - above-left) */ + a = lastcb; + b = prevpixel0 & 0xff; + c = lastprevcb; + lastprevcb = b; + if (a > b) { UINT8 tmp = a; a = b; b = tmp; } + if (a > c) { UINT8 tmp = a; a = c; c = tmp; } + if (b > c) { UINT8 tmp = b; b = c; c = tmp; } + lastcb = (pixel0 & 0xff) + b; + dest[x + 0] = (lasty << 8) | lastcb; + + /* compute previous, above, and (prev + above - above-left) */ + a = lasty; + b = prevpixel1 >> 8; + c = lastprevy; + lastprevy = b; + if (a > b) { UINT8 tmp = a; a = b; b = tmp; } + if (a > c) { UINT8 tmp = a; a = c; c = tmp; } + if (b > c) { UINT8 tmp = b; b = c; c = tmp; } + lasty = (pixel1 >> 8) + b; + + /* compute previous, above, and (prev + above - above-left) */ + a = lastcr; + b = prevpixel1 & 0xff; + c = lastprevcr; + lastprevcr = b; + if (a > b) { UINT8 tmp = a; a = b; b = tmp; } + if (a > c) { UINT8 tmp = a; a = c; c = tmp; } + if (b > c) { UINT8 tmp = b; b = c; c = tmp; } + lastcr = (pixel1 & 0xff) + b; + dest[x + 1] = (lasty << 8) | lastcr; + } + } + } + + return AVIERR_NONE; +} + + +static void u64toa(UINT64 val, char *output) +{ + UINT32 lo = (UINT32)(val & 0xffffffff); + UINT32 hi = (UINT32)(val >> 32); + if (hi != 0) + sprintf(output, "%X%08X", hi, lo); + else + sprintf(output, "%X", lo); +} + + +/*------------------------------------------------- + printf_chunk_recursive - print information + about a chunk recursively +-------------------------------------------------*/ + +static void printf_chunk_recursive(avi_file *file, avi_chunk *container, int indent) +{ + char size[20], offset[20]; + avi_chunk curchunk; + int avierr; + + /* iterate over chunks in this container */ + for (avierr = get_first_chunk(file, container, &curchunk); avierr == AVIERR_NONE; avierr = get_next_chunk(file, container, &curchunk)) + { + UINT32 chunksize = curchunk.size; + int recurse = FALSE; + + u64toa(curchunk.size, size); + u64toa(curchunk.offset, offset); + printf("%*schunk = %c%c%c%c, size=%s (%s)\n", indent, "", + (UINT8)(curchunk.type >> 0), + (UINT8)(curchunk.type >> 8), + (UINT8)(curchunk.type >> 16), + (UINT8)(curchunk.type >> 24), + size, offset); + + /* certain chunks are just containers; recurse into them */ + switch (curchunk.type) + { + /* basic containers */ + case CHUNKTYPE_RIFF: + case CHUNKTYPE_LIST: + printf("%*stype = %c%c%c%c\n", indent, "", + (UINT8)(curchunk.listtype >> 0), + (UINT8)(curchunk.listtype >> 8), + (UINT8)(curchunk.listtype >> 16), + (UINT8)(curchunk.listtype >> 24)); + recurse = TRUE; + chunksize = 0; + break; + } + + /* print data within the chunk */ + if (chunksize > 0 && curchunk.size < 1024 * 1024) + { + UINT8 *data = NULL; + int i; + + /* read the data for a chunk */ + avierr = read_chunk_data(file, &curchunk, &data); + if (avierr == AVIERR_NONE) + { + int bytes = MIN(512, chunksize); + for (i = 0; i < bytes; i++) + { + if (i % 16 == 0) printf("%*s ", indent, ""); + printf("%02X ", data[i]); + if (i % 16 == 15) printf("\n"); + } + if (chunksize % 16 != 0) printf("\n"); + free(data); + } + } + + /* if we're recursing, dive down */ + if (recurse) + printf_chunk_recursive(file, &curchunk, indent + 4); + } + + /* if we didn't get a legitimate error, indicate that */ + if (avierr != AVIERR_END) + printf("[chunk error %d]\n", avierr); +} diff --git a/src/lib/util/aviio.h b/src/lib/util/aviio.h new file mode 100644 index 00000000000..41f9eba826d --- /dev/null +++ b/src/lib/util/aviio.h @@ -0,0 +1,123 @@ +/*************************************************************************** + + aviio.h + + AVI movie format parsing helpers. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#ifndef __AVIIO_H__ + +#include "osdcore.h" +#include "bitmap.h" + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +enum _avi_error +{ + AVIERR_NONE = 0, + AVIERR_END, + AVIERR_INVALID_DATA, + AVIERR_NO_MEMORY, + AVIERR_READ_ERROR, + AVIERR_WRITE_ERROR, + AVIERR_STACK_TOO_DEEP, + AVIERR_UNSUPPORTED_FEATURE, + AVIERR_CANT_OPEN_FILE, + AVIERR_INCOMPATIBLE_AUDIO_STREAMS, + AVIERR_INVALID_SAMPLERATE, + AVIERR_INVALID_STREAM, + AVIERR_INVALID_FRAME, + AVIERR_INVALID_BITMAP, + AVIERR_UNSUPPORTED_VIDEO_FORMAT, + AVIERR_UNSUPPORTED_AUDIO_FORMAT, + AVIERR_EXCEEDED_SOUND_BUFFER +}; +typedef enum _avi_error avi_error; + + +enum _avi_datatype +{ + AVIDATA_VIDEO, + AVIDATA_AUDIO_CHAN0, + AVIDATA_AUDIO_CHAN1, + AVIDATA_AUDIO_CHAN2, + AVIDATA_AUDIO_CHAN3, + AVIDATA_AUDIO_CHAN4, + AVIDATA_AUDIO_CHAN5, + AVIDATA_AUDIO_CHAN6, + AVIDATA_AUDIO_CHAN7 +}; +typedef enum _avi_datatype avi_datatype; + + + +/*************************************************************************** + MACROS +***************************************************************************/ + +#define AVI_FOURCC(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24)) + +#define FORMAT_UYVY AVI_FOURCC('U','Y','V','Y') +#define FORMAT_VYUY AVI_FOURCC('V','Y','U','Y') +#define FORMAT_YUY2 AVI_FOURCC('Y','U','Y','2') +#define FORMAT_HFYU AVI_FOURCC('H','F','Y','U') + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +typedef struct _avi_file avi_file; + + +typedef struct _avi_movie_info avi_movie_info; +struct _avi_movie_info +{ + UINT32 video_format; /* format of video data */ + UINT32 video_timescale; /* timescale for video data */ + UINT32 video_sampletime; /* duration of a single video sample (frame) */ + UINT32 video_numsamples; /* total number of video samples */ + UINT32 video_width; /* width of the video */ + UINT32 video_height; /* height of the video */ + UINT32 video_depth; /* depth of the video */ + + UINT32 audio_format; /* format of audio data */ + UINT32 audio_timescale; /* timescale for audio data */ + UINT32 audio_sampletime; /* duration of a single audio sample */ + UINT32 audio_numsamples; /* total number of audio samples */ + UINT32 audio_channels; /* number of audio channels */ + UINT32 audio_samplebits; /* number of bits per channel */ + UINT32 audio_samplerate; /* sample rate of audio */ +}; + + + +/*************************************************************************** + PROTOTYPES +***************************************************************************/ + +avi_error avi_open(const char *filename, avi_file **file); +avi_error avi_create(const char *filename, const avi_movie_info *info, avi_file **file); +avi_error avi_close(avi_file *file); + +void avi_printf_chunks(avi_file *file); +const char *avi_error_string(avi_error err); + +const avi_movie_info *avi_get_movie_info(avi_file *file); +UINT32 avi_first_sample_in_frame(avi_file *file, UINT32 framenum); + +avi_error avi_read_video_frame_yuy16(avi_file *file, UINT32 framenum, bitmap_t *bitmap); +avi_error avi_read_sound_samples(avi_file *file, int channel, UINT32 firstsample, UINT32 numsamples, INT16 *output); + +avi_error avi_append_video_frame_yuy16(avi_file *file, const bitmap_t *bitmap); +avi_error avi_append_sound_samples(avi_file *file, int channel, const INT16 *samples, UINT32 numsamples); + +#endif diff --git a/src/lib/util/bitmap.c b/src/lib/util/bitmap.c new file mode 100644 index 00000000000..a7e5d5e46f8 --- /dev/null +++ b/src/lib/util/bitmap.c @@ -0,0 +1,263 @@ +/*************************************************************************** + + bitmap.c + + Core bitmap routines. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#include "bitmap.h" + + + +/*************************************************************************** + BITMAP ALLOCATION/CONFIGURATION +***************************************************************************/ + +/*------------------------------------------------- + bitmap_alloc -- allocate memory for a new + bitmap of the given format +-------------------------------------------------*/ + +bitmap_t *bitmap_alloc(int width, int height, bitmap_format format) +{ + return bitmap_alloc_slop(width, height, 0, 0, format); +} + + +/*------------------------------------------------- + bitmap_alloc_slop -- allocate a new bitmap with + additional slop on the borders +-------------------------------------------------*/ + +bitmap_t *bitmap_alloc_slop(int width, int height, int xslop, int yslop, bitmap_format format) +{ + int bpp = bitmap_format_to_bpp(format); + size_t allocbytes; + bitmap_t *bitmap; + int rowpixels; + + /* fail if invalid format */ + if (bpp == 0) + return NULL; + + /* allocate the bitmap itself */ + bitmap = malloc(sizeof(*bitmap)); + if (bitmap == NULL) + return NULL; + memset(bitmap, 0, sizeof(*bitmap)); + + /* round the width to a multiple of 8 and add some padding */ + rowpixels = (width + 2 * xslop + 7) & ~7; + + /* allocate memory for the bitmap itself */ + allocbytes = rowpixels * (height + 2 * yslop) * bpp / 8; + bitmap->alloc = malloc(allocbytes); + if (bitmap->alloc == NULL) + { + free(bitmap); + return NULL; + } + + /* clear to 0 by default */ + memset(bitmap->alloc, 0, allocbytes); + + /* fill in the data */ + bitmap->format = format; + bitmap->width = width; + bitmap->height = height; + bitmap->bpp = bpp; + bitmap->rowpixels = rowpixels; + bitmap->base = (UINT8 *)bitmap->alloc + (rowpixels * yslop + xslop) * (bpp / 8); + + return bitmap; +} + + +/*------------------------------------------------- + bitmap_wrap -- wrap an existing memory buffer + as a bitmap +-------------------------------------------------*/ + +bitmap_t *bitmap_wrap(void *base, int width, int height, int rowpixels, bitmap_format format) +{ + int bpp = bitmap_format_to_bpp(format); + bitmap_t *bitmap; + + /* fail if invalid format */ + if (bpp == 0) + return NULL; + + /* allocate memory */ + bitmap = malloc(sizeof(*bitmap)); + if (bitmap == NULL) + return NULL; + memset(bitmap, 0, sizeof(*bitmap)); + + /* fill in the data */ + bitmap->format = format; + bitmap->width = width; + bitmap->height = height; + bitmap->bpp = bpp; + bitmap->rowpixels = rowpixels; + bitmap->base = base; + + return bitmap; +} + + +/*------------------------------------------------- + bitmap_set_palette -- associate a palette with + a bitmap +-------------------------------------------------*/ + +void bitmap_set_palette(bitmap_t *bitmap, palette_t *palette) +{ + /* first dereference any existing palette */ + if (bitmap->palette != NULL) + { + palette_deref(bitmap->palette); + bitmap->palette = NULL; + } + + /* then reference any new palette */ + if (palette != NULL) + { + palette_ref(palette); + bitmap->palette = palette; + } +} + + +/*------------------------------------------------- + bitmap_free -- release memory allocated for + a bitmap +-------------------------------------------------*/ + +void bitmap_free(bitmap_t *bitmap) +{ + /* dereference the palette */ + if (bitmap->palette != NULL) + palette_deref(bitmap->palette); + + /* free any allocated memory */ + if (bitmap->alloc != NULL) + free(bitmap->alloc); + + /* free the bitmap */ + free(bitmap); +} + + + +/*************************************************************************** + BITMAP DRAWING +***************************************************************************/ + +/*------------------------------------------------- + bitmap_fill -- fill a bitmap with a solid + color +-------------------------------------------------*/ + +void bitmap_fill(bitmap_t *dest, const rectangle *cliprect, UINT32 color) +{ + rectangle fill; + int x, y; + + /* default to the whole bitmap */ + fill.min_x = fill.min_y = 0; + fill.max_x = dest->width - 1; + fill.max_y = dest->height - 1; + + /* if we have a cliprect, intersect with that */ + if (cliprect != NULL) + sect_rect(&fill, cliprect); + + /* early out if nothing to do */ + if (fill.min_x > fill.max_x || fill.min_y > fill.max_y) + return; + + /* based on the bpp go from there */ + switch (dest->bpp) + { + case 8: + /* 8bpp always uses memset */ + for (y = fill.min_y; y <= fill.max_y; y++) + memset(BITMAP_ADDR8(dest, y, fill.min_x), (UINT8)color, fill.max_x + 1 - fill.min_x); + break; + + case 16: + /* 16bpp can use memset if the bytes are equal */ + if ((UINT8)(color >> 8) == (UINT8)color) + { + for (y = fill.min_y; y <= fill.max_y; y++) + memset(BITMAP_ADDR16(dest, y, fill.min_x), (UINT8)color, (fill.max_x + 1 - fill.min_x) * 2); + } + else + { + for (y = fill.min_y; y <= fill.max_y; y++) + { + UINT16 *destrow = BITMAP_ADDR16(dest, y, 0); + for (x = fill.min_x; x <= fill.max_x; x++) + destrow[x] = (UINT16)color; + } + } + break; + + case 32: + /* 32bpp can use memset if the bytes are equal */ + if ((UINT8)(color >> 8) == (UINT8)color && (UINT16)(color >> 16) == (UINT16)color) + { + for (y = fill.min_y; y <= fill.max_y; y++) + memset(BITMAP_ADDR32(dest, y, fill.min_x), (UINT8)color, (fill.max_x + 1 - fill.min_x) * 4); + } + else + { + for (y = fill.min_y; y <= fill.max_y; y++) + { + UINT32 *destrow = BITMAP_ADDR32(dest, y, 0); + for (x = fill.min_x; x <= fill.max_x; x++) + destrow[x] = (UINT32)color; + } + } + break; + } +} + + + +/*************************************************************************** + BITMAP UTILITIES +***************************************************************************/ + +/*------------------------------------------------- + bitmap_format_to_bpp - given a format, return + the bpp +-------------------------------------------------*/ + +int bitmap_format_to_bpp(bitmap_format format) +{ + /* choose a depth for the format */ + switch (format) + { + case BITMAP_FORMAT_INDEXED8: + return 8; + + case BITMAP_FORMAT_INDEXED16: + case BITMAP_FORMAT_RGB15: + case BITMAP_FORMAT_YUY16: + return 16; + + case BITMAP_FORMAT_INDEXED32: + case BITMAP_FORMAT_RGB32: + case BITMAP_FORMAT_ARGB32: + return 32; + + default: + break; + } + return 0; +} diff --git a/src/lib/util/bitmap.h b/src/lib/util/bitmap.h new file mode 100644 index 00000000000..5b4a5badbd0 --- /dev/null +++ b/src/lib/util/bitmap.h @@ -0,0 +1,169 @@ +/*************************************************************************** + + bitmap.h + + Core bitmap routines. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#pragma once + +#ifndef __BITMAP_H__ +#define __BITMAP_H__ + +#include "osdcore.h" +#include "palette.h" + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* bitmap_format describes the various bitmap formats we use */ +enum _bitmap_format +{ + BITMAP_FORMAT_INVALID = 0, /* invalid format */ + BITMAP_FORMAT_INDEXED8, /* 8bpp indexed */ + BITMAP_FORMAT_INDEXED16, /* 16bpp indexed */ + BITMAP_FORMAT_INDEXED32, /* 32bpp indexed */ + BITMAP_FORMAT_RGB15, /* 15bpp 5-5-5 RGB */ + BITMAP_FORMAT_RGB32, /* 32bpp 8-8-8 RGB */ + BITMAP_FORMAT_ARGB32, /* 32bpp 8-8-8-8 ARGB */ + BITMAP_FORMAT_YUY16, /* 16bpp 8-8 Y/Cb, Y/Cr in sequence */ + BITMAP_FORMAT_LAST +}; +typedef enum _bitmap_format bitmap_format; + + +/* rectangles describe a bitmap portion */ +typedef struct _rectangle rectangle; +struct _rectangle +{ + int min_x; /* minimum X, or left coordinate */ + int max_x; /* maximum X, or right coordinate (inclusive) */ + int min_y; /* minimum Y, or top coordinate */ + int max_y; /* maximum Y, or bottom coordinate (inclusive) */ +}; + + +/* bitmaps describe a rectangular array of pixels */ +typedef struct _bitmap_t bitmap_t; +struct _bitmap_t +{ + void * alloc; /* pointer to allocated pixel memory */ + void * base; /* pointer to pixel (0,0) (adjusted for padding) */ + int rowpixels; /* pixels per row (including padding) */ + int width; /* width of the bitmap */ + int height; /* height of the bitmap */ + bitmap_format format; /* format of the bitmap */ + int bpp; /* bits per pixel */ + palette_t * palette; /* optional palette */ +}; + + + +/*************************************************************************** + MACROS +***************************************************************************/ + +/* Macros for accessing bitmap pixels */ +#define BITMAP_ADDR(bitmap, type, y, x) \ + ((type *)(bitmap)->base + (y) * (bitmap)->rowpixels + (x)) + +#define BITMAP_ADDR8(bitmap, y, x) BITMAP_ADDR(bitmap, UINT8, y, x) +#define BITMAP_ADDR16(bitmap, y, x) BITMAP_ADDR(bitmap, UINT16, y, x) +#define BITMAP_ADDR32(bitmap, y, x) BITMAP_ADDR(bitmap, UINT32, y, x) + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + + +/* ----- bitmap allocation ----- */ + +/* allocate a new bitmap of the given dimensions and format */ +bitmap_t *bitmap_alloc(int width, int height, bitmap_format format); + +/* allocate a new bitmap with additional slop on the borders */ +bitmap_t *bitmap_alloc_slop(int width, int height, int xslop, int yslop, bitmap_format format); + +/* wrap a bitmap object around an existing array of data */ +bitmap_t *bitmap_wrap(void *base, int width, int height, int rowpixels, bitmap_format format); + +/* associate a palette with a bitmap */ +void bitmap_set_palette(bitmap_t *bitmap, palette_t *palette); + +/* free allocated data for a bitmap */ +void bitmap_free(bitmap_t *bitmap); + + + +/* ----- bitmap drawing ----- */ + +/* fill a bitmap with a single color, clipping to the given rectangle */ +void bitmap_fill(bitmap_t *dest, const rectangle *clip, rgb_t color); + + + +/* ----- bitmap utilities ----- */ + +/* return the number of bits per pixel for a given bitmap format */ +int bitmap_format_to_bpp(bitmap_format format); + + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + sect_rect - compute the intersection of two + rectangles +-------------------------------------------------*/ + +INLINE void sect_rect(rectangle *dst, const rectangle *src) +{ + if (src->min_x > dst->min_x) dst->min_x = src->min_x; + if (src->max_x < dst->max_x) dst->max_x = src->max_x; + if (src->min_y > dst->min_y) dst->min_y = src->min_y; + if (src->max_y < dst->max_y) dst->max_y = src->max_y; +} + + +/*------------------------------------------------- + union_rect - compute the union of two + rectangles +-------------------------------------------------*/ + +INLINE void union_rect(rectangle *dst, const rectangle *src) +{ + if (src->min_x < dst->min_x) dst->min_x = src->min_x; + if (src->max_x > dst->max_x) dst->max_x = src->max_x; + if (src->min_y < dst->min_y) dst->min_y = src->min_y; + if (src->max_y > dst->max_y) dst->max_y = src->max_y; +} + + +/*------------------------------------------------- + plot_box - draw a filled rectangle into a + bitmap of arbitrary depth +-------------------------------------------------*/ + +INLINE void plot_box(bitmap_t *bitmap, int x, int y, int width, int height, UINT32 color) +{ + rectangle clip; + clip.min_x = x; + clip.min_y = y; + clip.max_x = x + width - 1; + clip.max_y = y + height - 1; + bitmap_fill(bitmap, &clip, color); +} + + +#endif /* __BITMAP_H__ */ diff --git a/src/lib/util/cdrom.c b/src/lib/util/cdrom.c new file mode 100644 index 00000000000..82287ad30eb --- /dev/null +++ b/src/lib/util/cdrom.c @@ -0,0 +1,617 @@ +/*************************************************************************** + + Generic MAME CD-ROM utilties - build IDE and SCSI CD-ROMs on top of this + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**************************************************************************** + + IMPORTANT: + "physical" block addresses are the actual addresses on the emulated CD. + "chd" block addresses are the block addresses in the CHD file. + Because we pad each track to a hunk boundry, these addressing + schemes will differ after track 1! + +***************************************************************************/ + +#include "cdrom.h" + + + +/*************************************************************************** + DEBUGGING +***************************************************************************/ + +#define VERBOSE (0) + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +struct _cdrom_file +{ + chd_file * chd; /* CHD file */ + cdrom_toc cdtoc; /* TOC for the CD */ + UINT32 hunksectors; /* sectors per hunk */ + UINT32 cachehunk; /* which hunk is cached */ + UINT8 * cache; /* cache of the current hunk */ +}; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +static chd_error read_sector_into_cache(cdrom_file *file, UINT32 lbasector, UINT32 *sectoroffs, UINT32 *tracknum); +static chd_error parse_metadata(chd_file *chd, cdrom_toc *toc); + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + physical_to_chd_lba - find the CHD LBA + and the track number +-------------------------------------------------*/ + +INLINE UINT32 physical_to_chd_lba(cdrom_file *file, UINT32 physlba, UINT32 *tracknum) +{ + UINT32 chdlba; + int track; + + /* loop until our current LBA is less than the start LBA of the next track */ + for (track = 0; track < file->cdtoc.numtrks; track++) + if (physlba < file->cdtoc.tracks[track + 1].physframeofs) + { + chdlba = physlba - file->cdtoc.tracks[track].physframeofs + file->cdtoc.tracks[track].chdframeofs; + if (tracknum != NULL) + *tracknum = track; + return chdlba; + } + + return physlba; +} + + + +/*************************************************************************** + BASE FUNCTIONALITY +***************************************************************************/ + +/*------------------------------------------------- + cdrom_open - "open" a CD-ROM file from an + already-opened CHD file +-------------------------------------------------*/ + +cdrom_file *cdrom_open(chd_file *chd) +{ + const chd_header *header = chd_get_header(chd); + int i; + cdrom_file *file; + UINT32 physofs, chdofs; + chd_error err; + + /* punt if no CHD */ + if (!chd) + return NULL; + + /* validate the CHD information */ + if (header->hunkbytes % CD_FRAME_SIZE != 0) + return NULL; + + /* allocate memory for the CD-ROM file */ + file = malloc(sizeof(cdrom_file)); + if (file == NULL) + return NULL; + + /* fill in the data */ + file->chd = chd; + file->hunksectors = header->hunkbytes / CD_FRAME_SIZE; + file->cachehunk = -1; + + /* read the CD-ROM metadata */ + err = parse_metadata(chd, &file->cdtoc); + if (err != CHDERR_NONE) + { + free(file); + return NULL; + } + + #if VERBOSE + logerror("CD has %d tracks\n", file->cdtoc.numtrks); + #endif + + /* calculate the starting frame for each track, keeping in mind that CHDMAN + pads tracks out with extra frames to fit hunk size boundries + */ + physofs = chdofs = 0; + for (i = 0; i < file->cdtoc.numtrks; i++) + { + file->cdtoc.tracks[i].physframeofs = physofs; + file->cdtoc.tracks[i].chdframeofs = chdofs; + + physofs += file->cdtoc.tracks[i].frames; + chdofs += file->cdtoc.tracks[i].frames; + chdofs += file->cdtoc.tracks[i].extraframes; + + #if VERBOSE + logerror("Track %02d is format %d subtype %d datasize %d subsize %d frames %d extraframes %d physofs %d chdofs %d\n", i+1, + file->cdtoc.tracks[i].trktype, + file->cdtoc.tracks[i].subtype, + file->cdtoc.tracks[i].datasize, + file->cdtoc.tracks[i].subsize, + file->cdtoc.tracks[i].frames, + file->cdtoc.tracks[i].extraframes, + file->cdtoc.tracks[i].physframeofs, + file->cdtoc.tracks[i].chdframeofs); + #endif + } + + /* fill out dummy entries for the last track to help our search */ + file->cdtoc.tracks[i].physframeofs = physofs; + file->cdtoc.tracks[i].chdframeofs = chdofs; + + /* allocate a cache */ + file->cache = malloc(chd_get_header(chd)->hunkbytes); + if (file->cache == NULL) + { + free(file); + return NULL; + } + + return file; +} + + +/*------------------------------------------------- + cdrom_close - "close" a CD-ROM file +-------------------------------------------------*/ + +void cdrom_close(cdrom_file *file) +{ + if (file == NULL) + return; + + /* free the cache */ + if (file->cache) + free(file->cache); + free(file); +} + + + +/*************************************************************************** + CORE READ ACCESS +***************************************************************************/ + +/*------------------------------------------------- + cdrom_read_data - read one or more sectors + from a CD-ROM +-------------------------------------------------*/ + +UINT32 cdrom_read_data(cdrom_file *file, UINT32 lbasector, void *buffer, UINT32 datatype) +{ + UINT32 tracktype, tracknum, sectoroffs; + chd_error err; + + if (file == NULL) + return 0; + + /* cache in the sector */ + err = read_sector_into_cache(file, lbasector, §oroffs, &tracknum); + if (err != CHDERR_NONE) + return 0; + + /* copy out the requested sector */ + tracktype = file->cdtoc.tracks[tracknum].trktype; + if ((datatype == tracktype) || (datatype == CD_TRACK_RAW_DONTCARE)) + { + memcpy(buffer, &file->cache[sectoroffs * CD_FRAME_SIZE], file->cdtoc.tracks[tracknum].datasize); + } + else + { + /* return 2048 bytes of mode1 data from a 2336 byte mode 1 or 2 raw sector */ + if ((datatype == CD_TRACK_MODE1) && ((tracktype == CD_TRACK_MODE1_RAW) || (tracktype == CD_TRACK_MODE2_RAW))) + { + memcpy(buffer, &file->cache[(sectoroffs * CD_FRAME_SIZE) + 16], 2048); + return 1; + } + + /* return 2048 bytes of mode1 data from a mode2 form1 sector */ + if ((datatype == CD_TRACK_MODE1) && (tracktype == CD_TRACK_MODE2_FORM1)) + { + memcpy(buffer, &file->cache[(sectoroffs * CD_FRAME_SIZE) + 24], 2048); + return 1; + } + + /* return mode 2 2336 byte data from a RAW sector */ + if ((datatype == CD_TRACK_MODE2) && ((tracktype == CD_TRACK_MODE1_RAW) || (tracktype == CD_TRACK_MODE2_RAW))) + { + memcpy(buffer, &file->cache[(sectoroffs * CD_FRAME_SIZE) + 16], 2048); + return 1; + } + + #if VERBOSE + logerror("CDROM: Conversion from type %d to type %d not supported!\n", tracktype, datatype); + #endif + return 0; + } + return 1; +} + + +/*------------------------------------------------- + cdrom_read_subcode - read subcode data for + a sector +-------------------------------------------------*/ + +UINT32 cdrom_read_subcode(cdrom_file *file, UINT32 lbasector, void *buffer) +{ + UINT32 sectoroffs, tracknum; + chd_error err; + + if (file == NULL) + return ~0; + + /* cache in the sector */ + err = read_sector_into_cache(file, lbasector, §oroffs, &tracknum); + if (err != CHDERR_NONE) + return 0; + + /* copy out the requested data */ + memcpy(buffer, &file->cache[(sectoroffs * CD_FRAME_SIZE) + file->cdtoc.tracks[tracknum].datasize], file->cdtoc.tracks[tracknum].subsize); + return 1; +} + + + +/*************************************************************************** + HANDY UTILITIES +***************************************************************************/ + +/*------------------------------------------------- + cdrom_get_track - get the track number + for a physical frame number +-------------------------------------------------*/ + +UINT32 cdrom_get_track(cdrom_file *file, UINT32 frame) +{ + UINT32 track = 0; + + if (file == NULL) + return ~0; + + /* convert to a CHD sector offset and get track information */ + physical_to_chd_lba(file, frame, &track); + return track; +} + + +/*------------------------------------------------- + cdrom_get_track_start - get the frame number + that a track starts at +-------------------------------------------------*/ + +UINT32 cdrom_get_track_start(cdrom_file *file, UINT32 track) +{ + if (file == NULL) + return ~0; + + /* handle lead-out specially */ + if (track == 0xaa) + track = file->cdtoc.numtrks; + + return file->cdtoc.tracks[track].physframeofs; +} + + + +/*************************************************************************** + TOC UTILITIES +***************************************************************************/ + +/*------------------------------------------------- + cdrom_get_last_track - returns the last track + number +-------------------------------------------------*/ + +int cdrom_get_last_track(cdrom_file *file) +{ + if (file == NULL) + return -1; + + return file->cdtoc.numtrks; +} + + +/*------------------------------------------------- + cdrom_get_adr_control - get the ADR | CONTROL + for a track +-------------------------------------------------*/ + +int cdrom_get_adr_control(cdrom_file *file, int track) +{ + if (file == NULL) + return -1; + + if (track == 0xaa || file->cdtoc.tracks[track].trktype == CD_TRACK_AUDIO) + { + return 0x10; // audio track, subchannel is position + } + + return 0x14; // data track, subchannel is position +} + + +/*------------------------------------------------- + cdrom_get_track_type - return the track type +-------------------------------------------------*/ + +int cdrom_get_track_type(cdrom_file *file, int track) +{ + if (file == NULL) + return -1; + + return file->cdtoc.tracks[track].trktype; +} + + +/*------------------------------------------------- + cdrom_get_toc - return the TOC data for a + CD-ROM +-------------------------------------------------*/ + +const cdrom_toc *cdrom_get_toc(cdrom_file *file) +{ + if (file == NULL) + return NULL; + + return &file->cdtoc; +} + + + +/*************************************************************************** + EXTRA UTILITIES +***************************************************************************/ + +/*------------------------------------------------- + cdrom_convert_type_string_to_track_info - + take a string and convert it into track type + and track data size +-------------------------------------------------*/ + +void cdrom_convert_type_string_to_track_info(const char *typestring, cdrom_track_info *info) +{ + if (!strcmp(typestring, "MODE1")) + { + info->trktype = CD_TRACK_MODE1; + info->datasize = 2048; + } + else if (!strcmp(typestring, "MODE1_RAW")) + { + info->trktype = CD_TRACK_MODE1_RAW; + info->datasize = 2352; + } + else if (!strcmp(typestring, "MODE2")) + { + info->trktype = CD_TRACK_MODE2; + info->datasize = 2336; + } + else if (!strcmp(typestring, "MODE2_FORM1")) + { + info->trktype = CD_TRACK_MODE2_FORM1; + info->datasize = 2048; + } + else if (!strcmp(typestring, "MODE2_FORM2")) + { + info->trktype = CD_TRACK_MODE2_FORM2; + info->datasize = 2324; + } + else if (!strcmp(typestring, "MODE2_FORM_MIX")) + { + info->trktype = CD_TRACK_MODE2_FORM_MIX; + info->datasize = 2336; + } + else if (!strcmp(typestring, "MODE2_RAW")) + { + info->trktype = CD_TRACK_MODE2_RAW; + info->datasize = 2352; + } + else if (!strcmp(typestring, "AUDIO")) + { + info->trktype = CD_TRACK_AUDIO; + info->datasize = 2352; + } +} + + +/*------------------------------------------------- + cdrom_convert_subtype_string_to_track_info - + take a string and convert it into track subtype + and track subcode data size +-------------------------------------------------*/ + +void cdrom_convert_subtype_string_to_track_info(const char *typestring, cdrom_track_info *info) +{ + if (!strcmp(typestring, "RW")) + { + info->subtype = CD_SUB_NORMAL; + info->subsize = 96; + } + else if (!strcmp(typestring, "RW_RAW")) + { + info->subtype = CD_SUB_RAW; + info->subsize = 96; + } +} + + +/*------------------------------------------------- + cdrom_get_type_string - get the string + associated with the given type +-------------------------------------------------*/ + +const char *cdrom_get_type_string(const cdrom_track_info *info) +{ + switch (info->trktype) + { + case CD_TRACK_MODE1: return "MODE1"; + case CD_TRACK_MODE1_RAW: return "MODE1_RAW"; + case CD_TRACK_MODE2: return "MODE2"; + case CD_TRACK_MODE2_FORM1: return "MODE2_FORM1"; + case CD_TRACK_MODE2_FORM2: return "MODE2_FORM2"; + case CD_TRACK_MODE2_FORM_MIX: return "MODE2_FORM_MIX"; + case CD_TRACK_MODE2_RAW: return "MODE2_RAW"; + case CD_TRACK_AUDIO: return "AUDIO"; + default: return "UNKNOWN"; + } +} + + +/*------------------------------------------------- + cdrom_get_subtype_string - get the string + associated with the given subcode type +-------------------------------------------------*/ + +const char *cdrom_get_subtype_string(const cdrom_track_info *info) +{ + switch (info->subtype) + { + case CD_SUB_NORMAL: return "RW"; + case CD_SUB_RAW: return "RW_RAW"; + default: return "NONE"; + } +} + + + +/*************************************************************************** + INTERNAL UTILITIES +***************************************************************************/ + +/*------------------------------------------------- + read_sector_into_cache - cache a sector at + the given physical LBA +-------------------------------------------------*/ + +static chd_error read_sector_into_cache(cdrom_file *file, UINT32 lbasector, UINT32 *sectoroffs, UINT32 *tracknum) +{ + UINT32 chdsector, hunknum; + chd_error err; + + /* convert to a CHD sector offset and get track information */ + *tracknum = 0; + chdsector = physical_to_chd_lba(file, lbasector, tracknum); + hunknum = chdsector / file->hunksectors; + *sectoroffs = chdsector % file->hunksectors; + + /* if we haven't cached this hunk, read it now */ + if (file->cachehunk != hunknum) + { + err = chd_read(file->chd, hunknum, file->cache); + if (err != CHDERR_NONE) + return err; + file->cachehunk = hunknum; + } + return CHDERR_NONE; +} + + +/*------------------------------------------------- + parse_metadata - parse metadata into the TOC + structure +-------------------------------------------------*/ + +static chd_error parse_metadata(chd_file *chd, cdrom_toc *toc) +{ + static UINT32 oldmetadata[CD_METADATA_WORDS], *mrp; + const chd_header *header = chd_get_header(chd); + UINT32 hunksectors = header->hunkbytes / CD_FRAME_SIZE; + char metadata[256]; + chd_error err; + int i; + + /* start with no tracks */ + for (toc->numtrks = 0; toc->numtrks < CD_MAX_TRACKS; toc->numtrks++) + { + cdrom_track_info *track = &toc->tracks[toc->numtrks]; + int tracknum = -1, frames = 0, hunks; + char type[11], subtype[11]; + + /* fetch the metadata for this track */ + err = chd_get_metadata(chd, CDROM_TRACK_METADATA_TAG, toc->numtrks, metadata, sizeof(metadata), NULL, NULL); + if (err != CHDERR_NONE) + break; + + /* parse the metadata */ + type[0] = subtype[0] = 0; + if (sscanf(metadata, CDROM_TRACK_METADATA_FORMAT, &tracknum, type, subtype, &frames) != 4) + return CHDERR_INVALID_DATA; + if (tracknum != toc->numtrks + 1) + return CHDERR_INVALID_DATA; + + /* extract the track type and determine the data size */ + track->trktype = CD_TRACK_MODE1; + track->datasize = 0; + cdrom_convert_type_string_to_track_info(type, track); + if (track->datasize == 0) + return CHDERR_INVALID_DATA; + + /* extract the subtype and determine the subcode data size */ + track->subtype = CD_SUB_NONE; + track->subsize = 0; + cdrom_convert_subtype_string_to_track_info(subtype, track); + + /* set the frames and extra frames data */ + track->frames = frames; + hunks = (frames + hunksectors - 1) / hunksectors; + track->extraframes = hunks * hunksectors - frames; + } + + /* if we got any tracks this way, we're done */ + if (toc->numtrks > 0) + return CHDERR_NONE; + + /* look for old-style metadata */ + err = chd_get_metadata(chd, CDROM_OLD_METADATA_TAG, 0, oldmetadata, sizeof(oldmetadata), NULL, NULL); + if (err != CHDERR_NONE) + return err; + + /* reconstruct the TOC from it */ + mrp = &oldmetadata[0]; + toc->numtrks = *mrp++; + + for (i = 0; i < CD_MAX_TRACKS; i++) + { + toc->tracks[i].trktype = *mrp++; + toc->tracks[i].subtype = *mrp++; + toc->tracks[i].datasize = *mrp++; + toc->tracks[i].subsize = *mrp++; + toc->tracks[i].frames = *mrp++; + toc->tracks[i].extraframes = *mrp++; + } + + /* TODO: I don't know why sometimes the data is one endian and sometimes another */ + if ((toc->numtrks < 0) || (toc->numtrks > CD_MAX_TRACKS)) + { + toc->numtrks = FLIPENDIAN_INT32(toc->numtrks); + for (i = 0; i < CD_MAX_TRACKS; i++) + { + toc->tracks[i].trktype = FLIPENDIAN_INT32(toc->tracks[i].trktype); + toc->tracks[i].subtype = FLIPENDIAN_INT32(toc->tracks[i].subtype); + toc->tracks[i].datasize = FLIPENDIAN_INT32(toc->tracks[i].datasize); + toc->tracks[i].subsize = FLIPENDIAN_INT32(toc->tracks[i].subsize); + toc->tracks[i].frames = FLIPENDIAN_INT32(toc->tracks[i].frames); + toc->tracks[i].extraframes = FLIPENDIAN_INT32(toc->tracks[i].extraframes); + } + } + + return CHDERR_NONE; +} diff --git a/src/lib/util/cdrom.h b/src/lib/util/cdrom.h new file mode 100644 index 00000000000..75f12e7099a --- /dev/null +++ b/src/lib/util/cdrom.h @@ -0,0 +1,139 @@ +/*************************************************************************** + + cdrom.h + + Generic MAME cd-rom implementation + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#pragma once + +#ifndef __CDROM_H__ +#define __CDROM_H__ + +#include "osdcore.h" +#include "chd.h" + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define CD_MAX_TRACKS (99) /* AFAIK the theoretical limit */ +#define CD_MAX_SECTOR_DATA (2352) +#define CD_MAX_SUBCODE_DATA (96) + +#define CD_FRAME_SIZE (CD_MAX_SECTOR_DATA + CD_MAX_SUBCODE_DATA) +#define CD_FRAMES_PER_HUNK (4) + +#define CD_METADATA_WORDS (1+(CD_MAX_TRACKS * 6)) +enum +{ + CD_TRACK_MODE1 = 0, /* mode 1 2048 bytes/sector */ + CD_TRACK_MODE1_RAW, /* mode 1 2352 bytes/sector */ + CD_TRACK_MODE2, /* mode 2 2336 bytes/sector */ + CD_TRACK_MODE2_FORM1, /* mode 2 2048 bytes/sector */ + CD_TRACK_MODE2_FORM2, /* mode 2 2324 bytes/sector */ + CD_TRACK_MODE2_FORM_MIX, /* mode 2 2336 bytes/sector */ + CD_TRACK_MODE2_RAW, /* mode 2 2352 bytes / sector */ + CD_TRACK_AUDIO, /* redbook audio track 2352 bytes/sector (588 samples) */ + + CD_TRACK_RAW_DONTCARE /* special flag for cdrom_read_data: just return me whatever is there */ +}; + +enum +{ + CD_SUB_NORMAL = 0, /* "cooked" 96 bytes per sector */ + CD_SUB_RAW, /* raw uninterleaved 96 bytes per sector */ + CD_SUB_NONE /* no subcode data stored */ +}; + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +typedef struct _cdrom_file cdrom_file; + + +typedef struct _cdrom_track_info cdrom_track_info; +struct _cdrom_track_info +{ + /* fields used by CHDMAN and in MAME */ + UINT32 trktype; /* track type */ + UINT32 subtype; /* subcode data type */ + UINT32 datasize; /* size of data in each sector of this track */ + UINT32 subsize; /* size of subchannel data in each sector of this track */ + UINT32 frames; /* number of frames in this track */ + UINT32 extraframes; /* number of "spillage" frames in this track */ + + /* fields used in MAME only */ + UINT32 physframeofs; /* frame number on the real CD this track starts at */ + UINT32 chdframeofs; /* frame number this track starts at on the CHD */ +}; + + +typedef struct _cdrom_toc cdrom_toc; +struct _cdrom_toc +{ + UINT32 numtrks; /* number of tracks */ + cdrom_track_info tracks[CD_MAX_TRACKS]; +}; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +/* base functionality */ +cdrom_file *cdrom_open(chd_file *chd); +void cdrom_close(cdrom_file *file); + +/* core read access */ +UINT32 cdrom_read_data(cdrom_file *file, UINT32 lbasector, void *buffer, UINT32 datatype); +UINT32 cdrom_read_subcode(cdrom_file *file, UINT32 lbasector, void *buffer); + +/* handy utilities */ +UINT32 cdrom_get_track(cdrom_file *file, UINT32 frame); +UINT32 cdrom_get_track_start(cdrom_file *file, UINT32 track); + +/* TOC utilities */ +int cdrom_get_last_track(cdrom_file *file); +int cdrom_get_adr_control(cdrom_file *file, int track); +int cdrom_get_track_type(cdrom_file *file, int track); +const cdrom_toc *cdrom_get_toc(cdrom_file *file); + +/* extra utilities */ +void cdrom_convert_type_string_to_track_info(const char *typestring, cdrom_track_info *info); +void cdrom_convert_subtype_string_to_track_info(const char *typestring, cdrom_track_info *info); +const char *cdrom_get_type_string(const cdrom_track_info *info); +const char *cdrom_get_subtype_string(const cdrom_track_info *info); + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +INLINE UINT32 lba_to_msf(UINT32 lba) +{ + UINT8 m, s, f; + + m = lba / (60 * 75); + lba -= m * (60 * 75); + s = lba / 75; + f = lba % 75; + + return ((m / 10) << 20) | ((m % 10) << 16) | + ((s / 10) << 12) | ((s % 10) << 8) | + ((f / 10) << 4) | ((f % 10) << 0); +} + +#endif // __CDROM_H__ + diff --git a/src/lib/util/chd.c b/src/lib/util/chd.c new file mode 100644 index 00000000000..b36bd5c70fe --- /dev/null +++ b/src/lib/util/chd.c @@ -0,0 +1,2860 @@ +/*************************************************************************** + + MAME Compressed Hunks of Data file format + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#include "chd.h" +#include "avcomp.h" +#include "md5.h" +#include "sha1.h" +#include <zlib.h> +#include <time.h> + + + +/*************************************************************************** + DEBUGGING +***************************************************************************/ + +#define PRINTF_MAX_HUNK (0) + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define MAP_STACK_ENTRIES 512 /* max number of entries to use on the stack */ +#define MAP_ENTRY_SIZE 16 /* V3 and later */ +#define OLD_MAP_ENTRY_SIZE 8 /* V1-V2 */ +#define METADATA_HEADER_SIZE 16 /* metadata header size */ +#define CRCMAP_HASH_SIZE 4095 /* number of CRC hashtable entries */ + +#define MAP_ENTRY_FLAG_TYPE_MASK 0x0f /* what type of hunk */ +#define MAP_ENTRY_FLAG_NO_CRC 0x10 /* no CRC is present */ + +#define MAP_ENTRY_TYPE_INVALID 0x00 /* invalid type */ +#define MAP_ENTRY_TYPE_COMPRESSED 0x01 /* standard compression */ +#define MAP_ENTRY_TYPE_UNCOMPRESSED 0x02 /* uncompressed data */ +#define MAP_ENTRY_TYPE_MINI 0x03 /* mini: use offset as raw data */ +#define MAP_ENTRY_TYPE_SELF_HUNK 0x04 /* same as another hunk in this file */ +#define MAP_ENTRY_TYPE_PARENT_HUNK 0x05 /* same as a hunk in the parent file */ + +#define CHD_V1_SECTOR_SIZE 512 /* size of a "sector" in the V1 header */ + +#define COOKIE_VALUE 0xbaadf00d +#define MAX_ZLIB_ALLOCS 64 + +#define END_OF_LIST_COOKIE "EndOfListCookie" + +#define NO_MATCH (~0) + + + +/*************************************************************************** + MACROS +***************************************************************************/ + +#define EARLY_EXIT(x) do { (void)(x); goto cleanup; } while (0) + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* interface to a codec */ +typedef struct _codec_interface codec_interface; +struct _codec_interface +{ + UINT32 compression; /* type of compression */ + const char *compname; /* name of the algorithm */ + UINT8 lossy; /* is this a lossy algorithm? */ + chd_error (*init)(chd_file *chd); /* codec initialize */ + void (*free)(chd_file *chd); /* codec free */ + chd_error (*compress)(chd_file *chd, const void *src, UINT32 *complen); /* compress data */ + chd_error (*decompress)(chd_file *chd, UINT32 complen, void *dst); /* decompress data */ + chd_error (*config)(chd_file *chd, int param, void *config); /* configure */ +}; + + +/* a single map entry */ +typedef struct _map_entry map_entry; +struct _map_entry +{ + UINT64 offset; /* offset within the file of the data */ + UINT32 crc; /* 32-bit CRC of the data */ + UINT32 length; /* length of the data */ + UINT8 flags; /* misc flags */ +}; + + +/* simple linked-list of hunks used for our CRC map */ +typedef struct _crcmap_entry crcmap_entry; +struct _crcmap_entry +{ + UINT32 hunknum; /* hunk number */ + crcmap_entry * next; /* next entry in list */ +}; + + +/* a single metadata entry */ +typedef struct _metadata_entry metadata_entry; +struct _metadata_entry +{ + UINT64 offset; /* offset within the file of the header */ + UINT64 next; /* offset within the file of the next header */ + UINT64 prev; /* offset within the file of the previous header */ + UINT32 length; /* length of the metadata */ + UINT32 metatag; /* metadata tag */ +}; + + +/* internal representation of an open CHD file */ +struct _chd_file +{ + UINT32 cookie; /* cookie, should equal COOKIE_VALUE */ + + core_file * file; /* handle to the open core file */ + UINT8 owns_file; /* flag indicating if this file should be closed on chd_close() */ + chd_header header; /* header, extracted from file */ + + chd_file * parent; /* pointer to parent file, or NULL */ + + map_entry * map; /* array of map entries */ + + UINT8 * cache; /* hunk cache pointer */ + UINT32 cachehunk; /* index of currently cached hunk */ + + UINT8 * compare; /* hunk compare pointer */ + UINT32 comparehunk; /* index of current compare data */ + + UINT8 * compressed; /* pointer to buffer for compressed data */ + const codec_interface * codecintf; /* interface to the codec */ + void * codecdata; /* opaque pointer to codec data */ + + crcmap_entry * crcmap; /* CRC map entries */ + crcmap_entry * crcfree; /* free list CRC entries */ + crcmap_entry ** crctable; /* table of CRC entries */ + + UINT32 maxhunk; /* maximum hunk accessed */ + + UINT8 compressing; /* are we compressing? */ + struct MD5Context compmd5; /* running MD5 during compression */ + struct sha1_ctx compsha1; /* running SHA1 during compression */ + UINT32 comphunk; /* next hunk we will compress */ + + UINT8 verifying; /* are we verifying? */ + struct MD5Context vermd5; /* running MD5 during verification */ + struct sha1_ctx versha1; /* running SHA1 during verification */ + UINT32 verhunk; /* next hunk we will verify */ + + osd_work_queue * workqueue; /* pointer to work queue for async operations */ + osd_work_item * workitem; /* active work item, or NULL if none */ + UINT32 async_hunknum; /* hunk index for asynchronous operations */ + void * async_buffer; /* buffer pointer for asynchronous operations */ +}; + + +/* codec-private data for the ZLIB codec */ +typedef struct _zlib_codec_data zlib_codec_data; +struct _zlib_codec_data +{ + z_stream inflater; + z_stream deflater; + UINT32 * allocptr[MAX_ZLIB_ALLOCS]; +}; + + +/* codec-private data for the A/V codec */ +typedef struct _av_codec_data av_codec_data; +struct _av_codec_data +{ + avcomp_state * compstate; + av_codec_decompress_config decompconfig; +}; + + + +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +static const UINT8 nullmd5[CHD_MD5_BYTES] = { 0 }; +static const UINT8 nullsha1[CHD_SHA1_BYTES] = { 0 }; + + + +/*************************************************************************** + PROTOTYPES +***************************************************************************/ + +/* internal async operations */ +static void *async_read_callback(void *param, int threadid); +static void *async_write_callback(void *param, int threadid); + +/* internal header operations */ +static chd_error header_validate(const chd_header *header); +static chd_error header_read(core_file *file, chd_header *header); +static chd_error header_write(core_file *file, const chd_header *header); + +/* internal hunk read/write */ +static chd_error hunk_read_into_cache(chd_file *chd, UINT32 hunknum); +static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *dest); +static chd_error hunk_write_from_memory(chd_file *chd, UINT32 hunknum, const UINT8 *src); + +/* internal map access */ +static chd_error map_write_initial(core_file *file, chd_file *parent, const chd_header *header); +static chd_error map_read(chd_file *chd); + +/* internal CRC map access */ +static void crcmap_init(chd_file *chd, int prepopulate); +static void crcmap_add_entry(chd_file *chd, UINT32 hunknum); +static UINT32 crcmap_find_hunk(chd_file *chd, UINT32 hunknum, UINT32 crc, const UINT8 *rawdata); + +/* metadata management */ +static chd_error metadata_find_entry(chd_file *chd, UINT32 metatag, UINT32 metaindex, metadata_entry *metaentry); +static chd_error metadata_set_previous_next(chd_file *chd, UINT64 prevoffset, UINT64 nextoffset); +static chd_error metadata_set_length(chd_file *chd, UINT64 offset, UINT32 length); + +/* zlib compression codec */ +static chd_error zlib_codec_init(chd_file *chd); +static void zlib_codec_free(chd_file *chd); +static chd_error zlib_codec_compress(chd_file *chd, const void *src, UINT32 *length); +static chd_error zlib_codec_decompress(chd_file *chd, UINT32 srclength, void *dest); +static voidpf zlib_fast_alloc(voidpf opaque, uInt items, uInt size); +static void zlib_fast_free(voidpf opaque, voidpf address); + +/* A/V compression codec */ +static chd_error av_codec_init(chd_file *chd); +static void av_codec_free(chd_file *chd); +static chd_error av_codec_compress(chd_file *chd, const void *src, UINT32 *length); +static chd_error av_codec_decompress(chd_file *chd, UINT32 srclength, void *dest); +static chd_error av_codec_config(chd_file *chd, int param, void *config); +static chd_error av_codec_postinit(chd_file *chd); + + + +/*************************************************************************** + CODEC INTERFACES +***************************************************************************/ + +static const codec_interface codec_interfaces[] = +{ + /* "none" or no compression */ + { + CHDCOMPRESSION_NONE, + "none", + FALSE, + NULL, + NULL, + NULL, + NULL, + NULL + }, + + /* standard zlib compression */ + { + CHDCOMPRESSION_ZLIB, + "zlib", + FALSE, + zlib_codec_init, + zlib_codec_free, + zlib_codec_compress, + zlib_codec_decompress, + NULL + }, + + /* zlib+ compression */ + { + CHDCOMPRESSION_ZLIB_PLUS, + "zlib+", + FALSE, + zlib_codec_init, + zlib_codec_free, + zlib_codec_compress, + zlib_codec_decompress, + NULL + }, + + /* a/v compression */ + { + CHDCOMPRESSION_AV, + "A/V", + TRUE, + av_codec_init, + av_codec_free, + av_codec_compress, + av_codec_decompress, + av_codec_config + } +}; + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + get_bigendian_uint64 - fetch a UINT64 from + the data stream in bigendian order +-------------------------------------------------*/ + +INLINE UINT64 get_bigendian_uint64(const UINT8 *base) +{ + return ((UINT64)base[0] << 56) | ((UINT64)base[1] << 48) | ((UINT64)base[2] << 40) | ((UINT64)base[3] << 32) | + ((UINT64)base[4] << 24) | ((UINT64)base[5] << 16) | ((UINT64)base[6] << 8) | (UINT64)base[7]; +} + + +/*------------------------------------------------- + put_bigendian_uint64 - write a UINT64 to + the data stream in bigendian order +-------------------------------------------------*/ + +INLINE void put_bigendian_uint64(UINT8 *base, UINT64 value) +{ + base[0] = value >> 56; + base[1] = value >> 48; + base[2] = value >> 40; + base[3] = value >> 32; + base[4] = value >> 24; + base[5] = value >> 16; + base[6] = value >> 8; + base[7] = value; +} + + +/*------------------------------------------------- + get_bigendian_uint32 - fetch a UINT32 from + the data stream in bigendian order +-------------------------------------------------*/ + +INLINE UINT32 get_bigendian_uint32(const UINT8 *base) +{ + return (base[0] << 24) | (base[1] << 16) | (base[2] << 8) | base[3]; +} + + +/*------------------------------------------------- + put_bigendian_uint32 - write a UINT32 to + the data stream in bigendian order +-------------------------------------------------*/ + +INLINE void put_bigendian_uint32(UINT8 *base, UINT32 value) +{ + base[0] = value >> 24; + base[1] = value >> 16; + base[2] = value >> 8; + base[3] = value; +} + + +/*------------------------------------------------- + get_bigendian_uint16 - fetch a UINT16 from + the data stream in bigendian order +-------------------------------------------------*/ + +INLINE UINT16 get_bigendian_uint16(const UINT8 *base) +{ + return (base[0] << 8) | base[1]; +} + + +/*------------------------------------------------- + put_bigendian_uint16 - write a UINT16 to + the data stream in bigendian order +-------------------------------------------------*/ + +INLINE void put_bigendian_uint16(UINT8 *base, UINT16 value) +{ + base[0] = value >> 8; + base[1] = value; +} + + +/*------------------------------------------------- + map_extract - extract a single map + entry from the datastream +-------------------------------------------------*/ + +INLINE void map_extract(const UINT8 *base, map_entry *entry) +{ + entry->offset = get_bigendian_uint64(&base[0]); + entry->crc = get_bigendian_uint32(&base[8]); + entry->length = get_bigendian_uint16(&base[12]) | (base[14] << 16); + entry->flags = base[15]; +} + + +/*------------------------------------------------- + map_assemble - write a single map + entry to the datastream +-------------------------------------------------*/ + +INLINE void map_assemble(UINT8 *base, map_entry *entry) +{ + put_bigendian_uint64(&base[0], entry->offset); + put_bigendian_uint32(&base[8], entry->crc); + put_bigendian_uint16(&base[12], entry->length); + base[14] = entry->length >> 16; + base[15] = entry->flags; +} + + +/*------------------------------------------------- + map_extract_old - extract a single map + entry in old format from the datastream +-------------------------------------------------*/ + +INLINE void map_extract_old(const UINT8 *base, map_entry *entry, UINT32 hunkbytes) +{ + entry->offset = get_bigendian_uint64(&base[0]); + entry->crc = 0; + entry->length = entry->offset >> 44; + entry->flags = MAP_ENTRY_FLAG_NO_CRC | ((entry->length == hunkbytes) ? MAP_ENTRY_TYPE_UNCOMPRESSED : MAP_ENTRY_TYPE_COMPRESSED); +#ifdef __MWERKS__ + entry->offset = entry->offset & 0x00000FFFFFFFFFFFLL; +#else + entry->offset = (entry->offset << 20) >> 20; +#endif +} + + +/*------------------------------------------------- + queue_async_operation - queue a new work + item +-------------------------------------------------*/ + +INLINE int queue_async_operation(chd_file *chd, osd_work_callback callback) +{ + /* if no queue yet, create one on the fly */ + if (chd->workqueue == NULL) + { + chd->workqueue = osd_work_queue_alloc(WORK_QUEUE_FLAG_IO); + if (chd->workqueue == NULL) + return FALSE; + } + + /* make sure we cleared out the previous item */ + if (chd->workitem != NULL) + return FALSE; + + /* create a new work item to run the job */ + chd->workitem = osd_work_item_queue(chd->workqueue, callback, chd, 0); + if (chd->workitem == NULL) + return FALSE; + + return TRUE; +} + + +/*------------------------------------------------- + wait_for_pending_async - wait for any pending + async +-------------------------------------------------*/ + +INLINE void wait_for_pending_async(chd_file *chd) +{ + /* if something is pending, wait for it */ + if (chd->workitem != NULL) + { + /* 10 seconds should be enough for anything! */ + int wait_successful = osd_work_item_wait(chd->workitem, 10 * osd_ticks_per_second()); + if (!wait_successful) + osd_break_into_debugger("Pending async operation never completed!"); + } +} + + + +/*************************************************************************** + CHD FILE MANAGEMENT +***************************************************************************/ + +/*------------------------------------------------- + chd_create_file - create a new CHD file +-------------------------------------------------*/ + +chd_error chd_create_file(core_file *file, UINT64 logicalbytes, UINT32 hunkbytes, UINT32 compression, chd_file *parent) +{ + chd_file *newchd = NULL; + chd_header header; + chd_error err; + int intfnum; + + /* verify parameters */ + if (file == NULL) + EARLY_EXIT(err = CHDERR_INVALID_PARAMETER); + if (parent == NULL && (logicalbytes == 0 || hunkbytes == 0)) + EARLY_EXIT(err = CHDERR_INVALID_PARAMETER); + + /* verify the compression type */ + for (intfnum = 0; intfnum < ARRAY_LENGTH(codec_interfaces); intfnum++) + if (codec_interfaces[intfnum].compression == compression) + break; + if (intfnum == ARRAY_LENGTH(codec_interfaces)) + EARLY_EXIT(err = CHDERR_INVALID_PARAMETER); + + /* if we have a parent, the sizes come from there */ + if (parent != NULL) + { + logicalbytes = parent->header.logicalbytes; + hunkbytes = parent->header.hunkbytes; + } + + /* if we have a parent, it must be V3 or later */ + if (parent != NULL && parent->header.version < 3) + EARLY_EXIT(err = CHDERR_UNSUPPORTED_VERSION); + + /* build the header */ + memset(&header, 0, sizeof(header)); + header.length = CHD_V3_HEADER_SIZE; + header.version = CHD_HEADER_VERSION; + header.flags = CHDFLAGS_IS_WRITEABLE; + header.compression = compression; + header.hunkbytes = hunkbytes; + header.totalhunks = (logicalbytes + hunkbytes - 1) / hunkbytes; + header.logicalbytes = logicalbytes; + + /* tweaks if there is a parent */ + if (parent != NULL) + { + header.flags |= CHDFLAGS_HAS_PARENT; + memcpy(&header.parentmd5[0], &parent->header.md5[0], sizeof(header.parentmd5)); + memcpy(&header.parentsha1[0], &parent->header.sha1[0], sizeof(header.parentsha1)); + } + + /* validate it */ + err = header_validate(&header); + if (err != CHDERR_NONE) + EARLY_EXIT(err); + + /* write the resulting header */ + err = header_write(file, &header); + if (err != CHDERR_NONE) + EARLY_EXIT(err); + + /* create an initial map */ + err = map_write_initial(file, parent, &header); + if (err != CHDERR_NONE) + EARLY_EXIT(err); + + /* if we have a parent, clone the metadata */ + if (parent != NULL) + { + /* open the new CHD via the standard mechanism */ + err = chd_open_file(file, CHD_OPEN_READWRITE, parent, &newchd); + if (err != CHDERR_NONE) + EARLY_EXIT(err); + + /* close the metadata */ + err = chd_clone_metadata(parent, newchd); + if (err != CHDERR_NONE) + EARLY_EXIT(err); + + /* close the CHD */ + chd_close(newchd); + } + + return CHDERR_NONE; + +cleanup: + if (newchd != NULL) + chd_close(newchd); + return err; +} + + +/*------------------------------------------------- + chd_open_file - open a CHD file for access +-------------------------------------------------*/ + +chd_error chd_open_file(core_file *file, int mode, chd_file *parent, chd_file **chd) +{ + chd_file *newchd = NULL; + chd_error err; + int intfnum; + + /* verify parameters */ + if (file == NULL) + EARLY_EXIT(err = CHDERR_INVALID_PARAMETER); + + /* punt if invalid parent */ + if (parent != NULL && parent->cookie != COOKIE_VALUE) + EARLY_EXIT(err = CHDERR_INVALID_PARAMETER); + + /* allocate memory for the final result */ + newchd = malloc(sizeof(**chd)); + if (newchd == NULL) + EARLY_EXIT(err = CHDERR_OUT_OF_MEMORY); + memset(newchd, 0, sizeof(*newchd)); + newchd->cookie = COOKIE_VALUE; + newchd->parent = parent; + newchd->file = file; + + /* now attempt to read the header */ + err = header_read(newchd->file, &newchd->header); + if (err != CHDERR_NONE) + EARLY_EXIT(err); + + /* validate the header */ + err = header_validate(&newchd->header); + if (err != CHDERR_NONE) + EARLY_EXIT(err); + + /* make sure we don't open a read-only file writeable */ + if (mode == CHD_OPEN_READWRITE && !(newchd->header.flags & CHDFLAGS_IS_WRITEABLE)) + EARLY_EXIT(err = CHDERR_FILE_NOT_WRITEABLE); + + /* also, never open an older version writeable */ + if (mode == CHD_OPEN_READWRITE && newchd->header.version < CHD_HEADER_VERSION) + EARLY_EXIT(err = CHDERR_UNSUPPORTED_VERSION); + + /* if we need a parent, make sure we have one */ + if (parent == NULL && (newchd->header.flags & CHDFLAGS_HAS_PARENT)) + EARLY_EXIT(err = CHDERR_REQUIRES_PARENT); + + /* make sure we have a valid parent */ + if (parent != NULL) + { + /* check MD5 if it isn't empty */ + if (memcmp(nullmd5, newchd->header.parentmd5, sizeof(newchd->header.parentmd5)) != 0 && + memcmp(nullmd5, newchd->parent->header.md5, sizeof(newchd->parent->header.md5)) != 0 && + memcmp(newchd->parent->header.md5, newchd->header.parentmd5, sizeof(newchd->header.parentmd5)) != 0) + EARLY_EXIT(err = CHDERR_INVALID_PARENT); + + /* check SHA1 if it isn't empty */ + if (memcmp(nullsha1, newchd->header.parentsha1, sizeof(newchd->header.parentsha1)) != 0 && + memcmp(nullsha1, newchd->parent->header.sha1, sizeof(newchd->parent->header.sha1)) != 0 && + memcmp(newchd->parent->header.sha1, newchd->header.parentsha1, sizeof(newchd->header.parentsha1)) != 0) + EARLY_EXIT(err = CHDERR_INVALID_PARENT); + } + + /* now read the hunk map */ + err = map_read(newchd); + if (err != CHDERR_NONE) + EARLY_EXIT(err); + + /* allocate and init the hunk cache */ + newchd->cache = malloc(newchd->header.hunkbytes); + newchd->compare = malloc(newchd->header.hunkbytes); + if (newchd->cache == NULL || newchd->compare == NULL) + EARLY_EXIT(err = CHDERR_OUT_OF_MEMORY); + newchd->cachehunk = ~0; + newchd->comparehunk = ~0; + + /* allocate the temporary compressed buffer */ + newchd->compressed = malloc(newchd->header.hunkbytes); + if (newchd->compressed == NULL) + EARLY_EXIT(err = CHDERR_OUT_OF_MEMORY); + + /* find the codec interface */ + for (intfnum = 0; intfnum < ARRAY_LENGTH(codec_interfaces); intfnum++) + if (codec_interfaces[intfnum].compression == newchd->header.compression) + { + newchd->codecintf = &codec_interfaces[intfnum]; + break; + } + if (intfnum == ARRAY_LENGTH(codec_interfaces)) + EARLY_EXIT(err = CHDERR_UNSUPPORTED_FORMAT); + + /* initialize the codec */ + if (newchd->codecintf->init != NULL) + err = (*newchd->codecintf->init)(newchd); + if (err != CHDERR_NONE) + EARLY_EXIT(err); + + /* all done */ + *chd = newchd; + return CHDERR_NONE; + +cleanup: + if (newchd != NULL) + chd_close(newchd); + return err; +} + + +/*------------------------------------------------- + chd_create - create a CHD file by + filename +-------------------------------------------------*/ + +chd_error chd_create(const char *filename, UINT64 logicalbytes, UINT32 hunkbytes, UINT32 compression, chd_file *parent) +{ + core_file *file = NULL; + file_error filerr; + chd_error chderr; + + filerr = core_fopen(filename, OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, &file); + if (filerr != FILERR_NONE) + { + chderr = CHDERR_FILE_NOT_FOUND; + goto cleanup; + } + + chderr = chd_create_file(file, logicalbytes, hunkbytes, compression, parent); + if (chderr != CHDERR_NONE) + goto cleanup; + +cleanup: + if (file != NULL) + core_fclose(file); + return chderr; +} + + +/*------------------------------------------------- + chd_open - open a CHD file by + filename +-------------------------------------------------*/ + +chd_error chd_open(const char *filename, int mode, chd_file *parent, chd_file **chd) +{ + chd_error err; + file_error filerr; + core_file *file = NULL; + UINT32 openflags; + + /* choose the proper mode */ + switch(mode) + { + case CHD_OPEN_READ: + openflags = OPEN_FLAG_READ; + break; + + case CHD_OPEN_READWRITE: + openflags = OPEN_FLAG_READ | OPEN_FLAG_WRITE; + break; + + default: + err = CHDERR_INVALID_PARAMETER; + goto cleanup; + } + + /* open the file */ + filerr = core_fopen(filename, openflags, &file); + if (filerr != FILERR_NONE) + { + err = CHDERR_FILE_NOT_FOUND; + goto cleanup; + } + + /* now open the CHD */ + err = chd_open_file(file, mode, parent, chd); + if (err != CHDERR_NONE) + goto cleanup; + + /* we now own this file */ + (*chd)->owns_file = TRUE; + +cleanup: + if ((err != CHDERR_NONE) && (file != NULL)) + core_fclose(file); + return err; +} + + +/*------------------------------------------------- + chd_close - close a CHD file for access +-------------------------------------------------*/ + +void chd_close(chd_file *chd) +{ + /* punt if NULL or invalid */ + if (chd == NULL || chd->cookie != COOKIE_VALUE) + return; + + /* wait for any pending async operations */ + wait_for_pending_async(chd); + + /* kill the work queue and any work item */ + if (chd->workitem != NULL) + osd_work_item_release(chd->workitem); + if (chd->workqueue != NULL) + osd_work_queue_free(chd->workqueue); + + /* deinit the codec */ + if (chd->codecintf != NULL && chd->codecintf->free != NULL) + (*chd->codecintf->free)(chd); + + /* free the compressed data buffer */ + if (chd->compressed != NULL) + free(chd->compressed); + + /* free the hunk cache and compare data */ + if (chd->compare != NULL) + free(chd->compare); + if (chd->cache != NULL) + free(chd->cache); + + /* free the hunk map */ + if (chd->map != NULL) + free(chd->map); + + /* free the CRC table */ + if (chd->crctable != NULL) + free(chd->crctable); + + /* free the CRC map */ + if (chd->crcmap != NULL) + free(chd->crcmap); + + /* close the file */ + if (chd->owns_file && chd->file != NULL) + core_fclose(chd->file); + +#if PRINTF_MAX_HUNK + printf("Max hunk = %d/%d\n", chd->maxhunk, chd->header.totalhunks); +#endif + + /* free our memory */ + free(chd); +} + + +/*------------------------------------------------- + chd_core_file - return the associated + core_file +-------------------------------------------------*/ + +core_file *chd_core_file(chd_file *chd) +{ + return chd->file; +} + + +/*------------------------------------------------- + chd_multi_filename - compute the indexed CHD + filename +-------------------------------------------------*/ + +void chd_multi_filename(const char *origname, char *finalname, int index) +{ + char *extension; + char findex[5]; + + /* copy the original name */ + strcpy(finalname, origname); + + /* determine the offset of the extension period */ + extension = strchr(finalname, '.'); + if (extension == NULL) + extension = finalname + strlen(finalname); + + /* compute our extension */ + sprintf(findex, ".%3d", index); + if (findex[1] == ' ') + findex[1] = 'c'; + if (findex[2] == ' ') + findex[2] = 'h'; + strcpy(extension, findex); +} + + + +/*************************************************************************** + CHD HEADER MANAGEMENT +***************************************************************************/ + +/*------------------------------------------------- + chd_get_header - return a pointer to the + extracted header data +-------------------------------------------------*/ + +const chd_header *chd_get_header(chd_file *chd) +{ + /* punt if NULL or invalid */ + if (chd == NULL || chd->cookie != COOKIE_VALUE) + return NULL; + + return &chd->header; +} + + +/*------------------------------------------------- + chd_set_header_file - write the current header to + the file +-------------------------------------------------*/ + +chd_error chd_set_header_file(core_file *file, const chd_header *header) +{ + chd_header oldheader; + chd_error err; + + /* validate the header */ + err = header_validate(header); + if (err != CHDERR_NONE) + EARLY_EXIT(err); + + /* read the old header */ + err = header_read(file, &oldheader); + if (err != CHDERR_NONE) + EARLY_EXIT(err); + + /* make sure we're only making valid changes */ + if (header->length != oldheader.length) + EARLY_EXIT(err = CHDERR_INVALID_PARAMETER); + if (header->version != oldheader.version) + EARLY_EXIT(err = CHDERR_INVALID_PARAMETER); + if (header->compression != oldheader.compression) + EARLY_EXIT(err = CHDERR_INVALID_PARAMETER); + if (header->hunkbytes != oldheader.hunkbytes) + EARLY_EXIT(err = CHDERR_INVALID_PARAMETER); + if (header->totalhunks != oldheader.totalhunks) + EARLY_EXIT(err = CHDERR_INVALID_PARAMETER); + if (header->metaoffset != oldheader.metaoffset) + EARLY_EXIT(err = CHDERR_INVALID_PARAMETER); + if (header->obsolete_hunksize != oldheader.obsolete_hunksize) + EARLY_EXIT(err = CHDERR_INVALID_PARAMETER); + + /* write the new header */ + err = header_write(file, header); + if (err != CHDERR_NONE) + EARLY_EXIT(err); + + return CHDERR_NONE; + +cleanup: + return err; +} + + +/*------------------------------------------------- + chd_set_header - write the current + header to the file +-------------------------------------------------*/ + +chd_error chd_set_header(const char *filename, const chd_header *header) +{ + core_file *file = NULL; + file_error filerr; + chd_error err; + + filerr = core_fopen(filename, OPEN_FLAG_READ | OPEN_FLAG_WRITE, &file); + if (filerr != FILERR_NONE) + { + err = CHDERR_FILE_NOT_FOUND; + goto cleanup; + } + + err = chd_set_header_file(file, header); + if (err != CHDERR_NONE) + goto cleanup; + +cleanup: + if (file != NULL) + core_fclose(file); + return err; +} + + + +/*************************************************************************** + CORE DATA READ/WRITE +***************************************************************************/ + +/*------------------------------------------------- + chd_read - read a single hunk from the CHD + file +-------------------------------------------------*/ + +chd_error chd_read(chd_file *chd, UINT32 hunknum, void *buffer) +{ + /* punt if NULL or invalid */ + if (chd == NULL || chd->cookie != COOKIE_VALUE) + return CHDERR_INVALID_PARAMETER; + + /* if we're past the end, fail */ + if (hunknum >= chd->header.totalhunks) + return CHDERR_HUNK_OUT_OF_RANGE; + + /* wait for any pending async operations */ + wait_for_pending_async(chd); + + /* perform the read */ + return hunk_read_into_memory(chd, hunknum, buffer); +} + + +/*------------------------------------------------- + chd_read_async - read a single hunk from the + CHD file asynchronously +-------------------------------------------------*/ + +chd_error chd_read_async(chd_file *chd, UINT32 hunknum, void *buffer) +{ + /* punt if NULL or invalid */ + if (chd == NULL || chd->cookie != COOKIE_VALUE) + return CHDERR_INVALID_PARAMETER; + + /* if we're past the end, fail */ + if (hunknum >= chd->header.totalhunks) + return CHDERR_HUNK_OUT_OF_RANGE; + + /* wait for any pending async operations */ + wait_for_pending_async(chd); + + /* set the async parameters */ + chd->async_hunknum = hunknum; + chd->async_buffer = buffer; + + /* queue the work item */ + if (queue_async_operation(chd, async_read_callback)) + return CHDERR_OPERATION_PENDING; + + /* if we fail, fall back on the sync version */ + return chd_read(chd, hunknum, buffer); +} + + +/*------------------------------------------------- + chd_write - write a single hunk to the CHD + file +-------------------------------------------------*/ + +chd_error chd_write(chd_file *chd, UINT32 hunknum, const void *buffer) +{ + /* punt if NULL or invalid */ + if (chd == NULL || chd->cookie != COOKIE_VALUE) + return CHDERR_INVALID_PARAMETER; + + /* if we're past the end, fail */ + if (hunknum >= chd->header.totalhunks) + return CHDERR_HUNK_OUT_OF_RANGE; + + /* wait for any pending async operations */ + wait_for_pending_async(chd); + + /* then write out the hunk */ + return hunk_write_from_memory(chd, hunknum, buffer); +} + + +/*------------------------------------------------- + chd_write_async - write a single hunk to the + CHD file asynchronously +-------------------------------------------------*/ + +chd_error chd_write_async(chd_file *chd, UINT32 hunknum, const void *buffer) +{ + /* punt if NULL or invalid */ + if (chd == NULL || chd->cookie != COOKIE_VALUE) + return CHDERR_INVALID_PARAMETER; + + /* if we're past the end, fail */ + if (hunknum >= chd->header.totalhunks) + return CHDERR_HUNK_OUT_OF_RANGE; + + /* wait for any pending async operations */ + wait_for_pending_async(chd); + + /* set the async parameters */ + chd->async_hunknum = hunknum; + chd->async_buffer = (void *)buffer; + + /* queue the work item */ + if (queue_async_operation(chd, async_write_callback)) + return CHDERR_OPERATION_PENDING; + + /* if we fail, fall back on the sync version */ + return chd_write(chd, hunknum, buffer); +} + + +/*------------------------------------------------- + chd_async_complete - get the result of a + completed work item and clear it out of the + system +-------------------------------------------------*/ + +chd_error chd_async_complete(chd_file *chd) +{ + void *result; + + /* if nothing present, return an error */ + if (chd->workitem == NULL) + return CHDERR_NO_ASYNC_OPERATION; + + /* wait for the work to complete */ + wait_for_pending_async(chd); + + /* get the result and free the work item */ + result = osd_work_item_result(chd->workitem); + osd_work_item_release(chd->workitem); + chd->workitem = NULL; + + return (chd_error)result; +} + + + +/*************************************************************************** + METADATA MANAGEMENT +***************************************************************************/ + +/*------------------------------------------------- + chd_get_metadata - get the indexed metadata + of the given type +-------------------------------------------------*/ + +chd_error chd_get_metadata(chd_file *chd, UINT32 searchtag, UINT32 searchindex, void *output, UINT32 outputlen, UINT32 *resultlen, UINT32 *resulttag) +{ + metadata_entry metaentry; + chd_error err; + UINT32 count; + + /* wait for any pending async operations */ + wait_for_pending_async(chd); + + /* if we didn't find it, just return */ + err = metadata_find_entry(chd, searchtag, searchindex, &metaentry); + if (err != CHDERR_NONE) + { + /* unless we're an old version and they are requesting hard disk metadata */ + if (chd->header.version < 3 && (searchtag == HARD_DISK_METADATA_TAG || searchtag == CHDMETATAG_WILDCARD) && searchindex == 0) + { + char faux_metadata[256]; + UINT32 faux_length; + + /* fill in the faux metadata */ + sprintf(faux_metadata, HARD_DISK_METADATA_FORMAT, chd->header.obsolete_cylinders, chd->header.obsolete_heads, chd->header.obsolete_sectors, chd->header.hunkbytes / chd->header.obsolete_hunksize); + faux_length = (UINT32)strlen(faux_metadata) + 1; + + /* copy the metadata itself */ + memcpy(output, faux_metadata, MIN(outputlen, faux_length)); + + /* return the length of the data and the tag */ + if (resultlen != NULL) + *resultlen = faux_length; + if (resulttag != NULL) + *resulttag = HARD_DISK_METADATA_TAG; + return CHDERR_NONE; + } + return err; + } + + /* read the metadata */ + outputlen = MIN(outputlen, metaentry.length); + core_fseek(chd->file, metaentry.offset + METADATA_HEADER_SIZE, SEEK_SET); + count = core_fread(chd->file, output, outputlen); + if (count != outputlen) + return CHDERR_READ_ERROR; + + /* return the length of the data and the tag */ + if (resultlen != NULL) + *resultlen = metaentry.length; + if (resulttag != NULL) + *resulttag = metaentry.metatag; + return CHDERR_NONE; +} + + +/*------------------------------------------------- + chd_set_metadata - write the indexed metadata + of the given type +-------------------------------------------------*/ + +chd_error chd_set_metadata(chd_file *chd, UINT32 metatag, UINT32 metaindex, const void *inputbuf, UINT32 inputlen) +{ + UINT8 raw_meta_header[METADATA_HEADER_SIZE]; + metadata_entry metaentry; + chd_error err; + UINT64 offset; + UINT32 count; + + /* if the disk is an old version, punt */ + if (chd->header.version < 3) + return CHDERR_NOT_SUPPORTED; + + /* if the disk isn't writeable, punt */ + if (!(chd->header.flags & CHDFLAGS_IS_WRITEABLE)) + return CHDERR_FILE_NOT_WRITEABLE; + + /* must write at least 1 byte */ + if (inputlen < 1) + return CHDERR_INVALID_PARAMETER; + + /* wait for any pending async operations */ + wait_for_pending_async(chd); + + /* find the entry if it already exists */ + err = metadata_find_entry(chd, metatag, metaindex, &metaentry); + + /* if it's there and it fits, just overwrite */ + if (err == CHDERR_NONE && inputlen <= metaentry.length) + { + /* overwrite the original data with our new input data */ + core_fseek(chd->file, metaentry.offset + METADATA_HEADER_SIZE, SEEK_SET); + count = core_fwrite(chd->file, inputbuf, inputlen); + if (count != inputlen) + return CHDERR_WRITE_ERROR; + + /* if the lengths don't match, we need to update the length in our header */ + if (inputlen != metaentry.length) + { + err = metadata_set_length(chd, metaentry.offset, inputlen); + if (err != CHDERR_NONE) + return err; + } + return CHDERR_NONE; + } + + /* if we already have an entry, unlink it */ + if (err == CHDERR_NONE) + { + err = metadata_set_previous_next(chd, metaentry.prev, metaentry.next); + if (err != CHDERR_NONE) + return err; + } + + /* now build us a new entry */ + put_bigendian_uint32(&raw_meta_header[0], metatag); + put_bigendian_uint32(&raw_meta_header[4], inputlen); + put_bigendian_uint64(&raw_meta_header[8], (err == CHDERR_NONE) ? metaentry.next : 0); + + /* write out the new header */ + offset = core_fsize(chd->file); + core_fseek(chd->file, offset, SEEK_SET); + count = core_fwrite(chd->file, raw_meta_header, sizeof(raw_meta_header)); + if (count != sizeof(raw_meta_header)) + return CHDERR_WRITE_ERROR; + + /* follow that with the data */ + core_fseek(chd->file, offset + METADATA_HEADER_SIZE, SEEK_SET); + count = core_fwrite(chd->file, inputbuf, inputlen); + if (count != inputlen) + return CHDERR_WRITE_ERROR; + + /* set the previous entry to point to us */ + err = metadata_set_previous_next(chd, metaentry.prev, offset); + if (err != CHDERR_NONE) + return err; + + return CHDERR_NONE; +} + + +/*------------------------------------------------- + chd_clone_metadata - clone the metadata from + one CHD to a second +-------------------------------------------------*/ + +chd_error chd_clone_metadata(chd_file *source, chd_file *dest) +{ + UINT32 metatag, metasize, metaindex; + UINT8 metabuffer[1024]; + chd_error err; + + /* clone the metadata */ + for (metaindex = 0; ; metaindex++) + { + /* fetch the next piece of metadata */ + err = chd_get_metadata(source, CHDMETATAG_WILDCARD, metaindex, metabuffer, sizeof(metabuffer), &metasize, &metatag); + if (err != CHDERR_NONE) + { + if (err == CHDERR_METADATA_NOT_FOUND) + err = CHDERR_NONE; + break; + } + + /* if that fit, just write it back from the temporary buffer */ + if (metasize <= sizeof(metabuffer)) + { + /* write it to the target */ + err = chd_set_metadata(dest, metatag, CHD_METAINDEX_APPEND, metabuffer, metasize); + if (err != CHDERR_NONE) + break; + } + + /* otherwise, allocate a bigger temporary buffer */ + else + { + UINT8 *allocbuffer = malloc(metasize); + if (allocbuffer == NULL) + { + err = CHDERR_OUT_OF_MEMORY; + break; + } + + /* re-read the whole thing */ + err = chd_get_metadata(source, CHDMETATAG_WILDCARD, metaindex, allocbuffer, metasize, &metasize, &metatag); + if (err != CHDERR_NONE) + { + free(allocbuffer); + break; + } + + /* write it to the target */ + err = chd_set_metadata(dest, metatag, CHD_METAINDEX_APPEND, allocbuffer, metasize); + free(allocbuffer); + if (err != CHDERR_NONE) + break; + } + } + return err; +} + + + +/*************************************************************************** + COMPRESSION MANAGEMENT +***************************************************************************/ + +/*------------------------------------------------- + chd_compress_begin - begin compressing data + into a CHD +-------------------------------------------------*/ + +chd_error chd_compress_begin(chd_file *chd) +{ + chd_error err; + + /* verify parameters */ + if (chd == NULL) + return CHDERR_INVALID_PARAMETER; + + /* wait for any pending async operations */ + wait_for_pending_async(chd); + + /* mark the CHD writeable and write the updated header */ + chd->header.flags |= CHDFLAGS_IS_WRITEABLE; + err = header_write(chd->file, &chd->header); + if (err != CHDERR_NONE) + return err; + + /* create CRC maps for the new CHD and the parent */ + crcmap_init(chd, FALSE); + if (chd->parent != NULL) + crcmap_init(chd->parent, TRUE); + + /* init the MD5/SHA1 computations */ + MD5Init(&chd->compmd5); + sha1_init(&chd->compsha1); + chd->compressing = TRUE; + chd->comphunk = 0; + + return CHDERR_NONE; +} + + +/*------------------------------------------------- + chd_compress_hunk - append data to a CHD + that is being compressed +-------------------------------------------------*/ + +chd_error chd_compress_hunk(chd_file *chd, const void *data, double *curratio) +{ + UINT32 thishunk = chd->comphunk++; + UINT64 sourceoffset = (UINT64)thishunk * (UINT64)chd->header.hunkbytes; + UINT32 bytestochecksum; + const void *crcdata; + chd_error err; + + /* error if in the wrong state */ + if (!chd->compressing) + return CHDERR_INVALID_STATE; + + /* write out the hunk */ + err = hunk_write_from_memory(chd, thishunk, data); + if (err != CHDERR_NONE) + return err; + + /* if we are lossy, then we need to use the decompressed version in */ + /* the cache as our MD5/SHA1 source */ + crcdata = chd->codecintf->lossy ? chd->cache : data; + + /* update the MD5/SHA1 */ + bytestochecksum = chd->header.hunkbytes; + if (sourceoffset + chd->header.hunkbytes > chd->header.logicalbytes) + { + if (sourceoffset >= chd->header.logicalbytes) + bytestochecksum = 0; + else + bytestochecksum = chd->header.logicalbytes - sourceoffset; + } + if (bytestochecksum > 0) + { + MD5Update(&chd->compmd5, crcdata, bytestochecksum); + sha1_update(&chd->compsha1, bytestochecksum, crcdata); + } + + /* update our CRC map */ + if ((chd->map[thishunk].flags & MAP_ENTRY_FLAG_TYPE_MASK) != MAP_ENTRY_TYPE_SELF_HUNK && + (chd->map[thishunk].flags & MAP_ENTRY_FLAG_TYPE_MASK) != MAP_ENTRY_TYPE_PARENT_HUNK) + crcmap_add_entry(chd, thishunk); + + /* update the ratio */ + if (curratio != NULL) + { + UINT64 curlength = core_fsize(chd->file); + *curratio = 1.0 - (double)curlength / (double)((UINT64)chd->comphunk * (UINT64)chd->header.hunkbytes); + } + + return CHDERR_NONE; +} + + +/*------------------------------------------------- + chd_compress_finish - complete compression of + a CHD +-------------------------------------------------*/ + +chd_error chd_compress_finish(chd_file *chd) +{ + /* error if in the wrong state */ + if (!chd->compressing) + return CHDERR_INVALID_STATE; + + /* compute the final MD5/SHA1 values */ + MD5Final(chd->header.md5, &chd->compmd5); + sha1_final(&chd->compsha1); + sha1_digest(&chd->compsha1, SHA1_DIGEST_SIZE, chd->header.sha1); + + /* turn off the writeable flag and re-write the header */ + chd->header.flags &= ~CHDFLAGS_IS_WRITEABLE; + chd->compressing = FALSE; + return header_write(chd->file, &chd->header); +} + + + +/*************************************************************************** + VERIFICATION +***************************************************************************/ + +/*------------------------------------------------- + chd_verify_begin - begin compressing data + into a CHD +-------------------------------------------------*/ + +chd_error chd_verify_begin(chd_file *chd) +{ + /* verify parameters */ + if (chd == NULL) + return CHDERR_INVALID_PARAMETER; + + /* if this is a writeable file image, we can't verify */ + if (chd->header.flags & CHDFLAGS_IS_WRITEABLE) + return CHDERR_CANT_VERIFY; + + /* wait for any pending async operations */ + wait_for_pending_async(chd); + + /* init the MD5/SHA1 computations */ + MD5Init(&chd->vermd5); + sha1_init(&chd->versha1); + chd->verifying = TRUE; + chd->verhunk = 0; + + return CHDERR_NONE; +} + + +/*------------------------------------------------- + chd_verify_hunk - verify the next hunk in + the CHD +-------------------------------------------------*/ + +chd_error chd_verify_hunk(chd_file *chd) +{ + UINT32 thishunk = chd->verhunk++; + UINT64 hunkoffset = (UINT64)thishunk * (UINT64)chd->header.hunkbytes; + map_entry *entry; + chd_error err; + + /* error if in the wrong state */ + if (!chd->verifying) + return CHDERR_INVALID_STATE; + + /* read the hunk into the cache */ + err = hunk_read_into_cache(chd, thishunk); + if (err != CHDERR_NONE) + return err; + + /* update the MD5/SHA1 */ + if (hunkoffset < chd->header.logicalbytes) + { + UINT64 bytestochecksum = MIN(chd->header.hunkbytes, chd->header.logicalbytes - hunkoffset); + if (bytestochecksum > 0) + { + MD5Update(&chd->vermd5, chd->cache, bytestochecksum); + sha1_update(&chd->versha1, bytestochecksum, chd->cache); + } + } + + /* validate the CRC if we have one */ + entry = &chd->map[thishunk]; + if (!(entry->flags & MAP_ENTRY_FLAG_NO_CRC) && entry->crc != crc32(0, chd->cache, chd->header.hunkbytes)) + return CHDERR_DECOMPRESSION_ERROR; + + return CHDERR_NONE; +} + + +/*------------------------------------------------- + chd_verify_finish - finish verification of + the CHD +-------------------------------------------------*/ + +chd_error chd_verify_finish(chd_file *chd, UINT8 *finalmd5, UINT8 *finalsha1) +{ + /* error if in the wrong state */ + if (!chd->verifying) + return CHDERR_INVALID_STATE; + + /* compute the final MD5 */ + if (finalmd5 != NULL) + MD5Final(finalmd5, &chd->vermd5); + + /* compute the final SHA1 */ + if (finalsha1 != NULL) + { + sha1_final(&chd->versha1); + sha1_digest(&chd->versha1, SHA1_DIGEST_SIZE, finalsha1); + } + + /* return an error */ + chd->verifying = FALSE; + return (chd->verhunk < chd->header.totalhunks) ? CHDERR_VERIFY_INCOMPLETE : CHDERR_NONE; +} + + + +/*************************************************************************** + CODEC INTERFACES +***************************************************************************/ + +/*------------------------------------------------- + chd_codec_config - set internal codec + parameters +-------------------------------------------------*/ + +chd_error chd_codec_config(chd_file *chd, int param, void *config) +{ + /* wait for any pending async operations */ + wait_for_pending_async(chd); + + /* if the codec has a configuration callback, call through to it */ + if (chd->codecintf->config != NULL) + return (*chd->codecintf->config)(chd, param, config); + + return CHDERR_INVALID_PARAMETER; +} + + +/*------------------------------------------------- + chd_get_codec_name - get the name of a + particular codec +-------------------------------------------------*/ + +const char *chd_get_codec_name(UINT32 codec) +{ + int intfnum; + + /* look for a matching codec and return its string */ + for (intfnum = 0; intfnum < ARRAY_LENGTH(codec_interfaces); intfnum++) + if (codec_interfaces[intfnum].compression == codec) + return codec_interfaces[intfnum].compname; + + return "Unknown"; +} + + + +/*************************************************************************** + INTERNAL ASYNC OPERATIONS +***************************************************************************/ + +/*------------------------------------------------- + async_read_callback - asynchronous reading + callback +-------------------------------------------------*/ + +static void *async_read_callback(void *param, int threadid) +{ + chd_file *chd = param; + chd_error err; + + /* read the hunk into the cache */ + err = hunk_read_into_memory(chd, chd->async_hunknum, chd->async_buffer); + + /* return the error */ + return (void *)err; +} + + +/*------------------------------------------------- + async_write_callback - asynchronous writing + callback +-------------------------------------------------*/ + +static void *async_write_callback(void *param, int threadid) +{ + chd_file *chd = param; + chd_error err; + + /* write the hunk from memory */ + err = hunk_write_from_memory(chd, chd->async_hunknum, chd->async_buffer); + + /* return the error */ + return (void *)err; +} + + + +/*************************************************************************** + INTERNAL HEADER OPERATIONS +***************************************************************************/ + +/*------------------------------------------------- + header_validate - check the validity of a + CHD header +-------------------------------------------------*/ + +static chd_error header_validate(const chd_header *header) +{ + int intfnum; + + /* require a valid version */ + if (header->version == 0 || header->version > CHD_HEADER_VERSION) + return CHDERR_UNSUPPORTED_VERSION; + + /* require a valid length */ + if ((header->version == 1 && header->length != CHD_V1_HEADER_SIZE) || + (header->version == 2 && header->length != CHD_V2_HEADER_SIZE) || + (header->version == 3 && header->length != CHD_V3_HEADER_SIZE)) + return CHDERR_INVALID_PARAMETER; + + /* require valid flags */ + if (header->flags & CHDFLAGS_UNDEFINED) + return CHDERR_INVALID_PARAMETER; + + /* require a supported compression mechanism */ + for (intfnum = 0; intfnum < ARRAY_LENGTH(codec_interfaces); intfnum++) + if (codec_interfaces[intfnum].compression == header->compression) + break; + if (intfnum == ARRAY_LENGTH(codec_interfaces)) + return CHDERR_INVALID_PARAMETER; + + /* require a valid hunksize */ + if (header->hunkbytes == 0 || header->hunkbytes >= 65536 * 256) + return CHDERR_INVALID_PARAMETER; + + /* require a valid hunk count */ + if (header->totalhunks == 0) + return CHDERR_INVALID_PARAMETER; + + /* require a valid MD5 and/or SHA1 if we're using a parent */ + if ((header->flags & CHDFLAGS_HAS_PARENT) && memcmp(header->parentmd5, nullmd5, sizeof(nullmd5)) == 0 && memcmp(header->parentsha1, nullsha1, sizeof(nullsha1)) == 0) + return CHDERR_INVALID_PARAMETER; + + /* if we're V3 or later, the obsolete fields must be 0 */ + if (header->version >= 3 && + (header->obsolete_cylinders != 0 || header->obsolete_sectors != 0 || + header->obsolete_heads != 0 || header->obsolete_hunksize != 0)) + return CHDERR_INVALID_PARAMETER; + + /* if we're pre-V3, the obsolete fields must NOT be 0 */ + if (header->version < 3 && + (header->obsolete_cylinders == 0 || header->obsolete_sectors == 0 || + header->obsolete_heads == 0 || header->obsolete_hunksize == 0)) + return CHDERR_INVALID_PARAMETER; + + return CHDERR_NONE; +} + + +/*------------------------------------------------- + header_read - read a CHD header into the + internal data structure +-------------------------------------------------*/ + +static chd_error header_read(core_file *file, chd_header *header) +{ + UINT8 rawheader[CHD_MAX_HEADER_SIZE]; + UINT32 count; + + /* punt if NULL */ + if (!header) + return CHDERR_INVALID_PARAMETER; + + /* punt if invalid file */ + if (!file) + return CHDERR_INVALID_FILE; + + /* seek and read */ + core_fseek(file, 0, SEEK_SET); + count = core_fread(file, rawheader, sizeof(rawheader)); + if (count != sizeof(rawheader)) + return CHDERR_READ_ERROR; + + /* verify the tag */ + if (strncmp((char *)rawheader, "MComprHD", 8) != 0) + return CHDERR_INVALID_DATA; + + /* extract the direct data */ + memset(header, 0, sizeof(*header)); + header->length = get_bigendian_uint32(&rawheader[8]); + header->version = get_bigendian_uint32(&rawheader[12]); + + /* make sure it's a version we understand */ + if (header->version == 0 || header->version > CHD_HEADER_VERSION) + return CHDERR_UNSUPPORTED_VERSION; + + /* make sure the length is expected */ + if ((header->version == 1 && header->length != CHD_V1_HEADER_SIZE) || + (header->version == 2 && header->length != CHD_V2_HEADER_SIZE) || + (header->version == 3 && header->length != CHD_V3_HEADER_SIZE)) + return CHDERR_INVALID_DATA; + + /* extract the common data */ + header->flags = get_bigendian_uint32(&rawheader[16]); + header->compression = get_bigendian_uint32(&rawheader[20]); + memcpy(header->md5, &rawheader[44], CHD_MD5_BYTES); + memcpy(header->parentmd5, &rawheader[60], CHD_MD5_BYTES); + + /* extract the V1/V2-specific data */ + if (header->version < 3) + { + int seclen = (header->version == 1) ? CHD_V1_SECTOR_SIZE : get_bigendian_uint32(&rawheader[76]); + header->obsolete_hunksize = get_bigendian_uint32(&rawheader[24]); + header->totalhunks = get_bigendian_uint32(&rawheader[28]); + header->obsolete_cylinders = get_bigendian_uint32(&rawheader[32]); + header->obsolete_heads = get_bigendian_uint32(&rawheader[36]); + header->obsolete_sectors = get_bigendian_uint32(&rawheader[40]); + header->logicalbytes = (UINT64)header->obsolete_cylinders * (UINT64)header->obsolete_heads * (UINT64)header->obsolete_sectors * (UINT64)seclen; + header->hunkbytes = seclen * header->obsolete_hunksize; + header->metaoffset = 0; + } + + /* extract the V3-specific data */ + else + { + header->totalhunks = get_bigendian_uint32(&rawheader[24]); + header->logicalbytes = get_bigendian_uint64(&rawheader[28]); + header->metaoffset = get_bigendian_uint64(&rawheader[36]); + header->hunkbytes = get_bigendian_uint32(&rawheader[76]); + memcpy(header->sha1, &rawheader[80], CHD_SHA1_BYTES); + memcpy(header->parentsha1, &rawheader[100], CHD_SHA1_BYTES); + } + + /* guess it worked */ + return CHDERR_NONE; +} + + +/*------------------------------------------------- + header_write - write a CHD header from the + internal data structure +-------------------------------------------------*/ + +static chd_error header_write(core_file *file, const chd_header *header) +{ + UINT8 rawheader[CHD_MAX_HEADER_SIZE]; + UINT32 count; + + /* punt if NULL */ + if (!header) + return CHDERR_INVALID_PARAMETER; + + /* punt if invalid file */ + if (!file) + return CHDERR_INVALID_FILE; + + /* only support writing modern headers */ + if (header->version != 3) + return CHDERR_INVALID_PARAMETER; + + /* assemble the data */ + memset(rawheader, 0, sizeof(rawheader)); + memcpy(rawheader, "MComprHD", 8); + + put_bigendian_uint32(&rawheader[8], CHD_V3_HEADER_SIZE); + put_bigendian_uint32(&rawheader[12], header->version); + put_bigendian_uint32(&rawheader[16], header->flags); + put_bigendian_uint32(&rawheader[20], header->compression); + put_bigendian_uint32(&rawheader[24], header->totalhunks); + put_bigendian_uint64(&rawheader[28], header->logicalbytes); + put_bigendian_uint64(&rawheader[36], header->metaoffset); + memcpy(&rawheader[44], header->md5, CHD_MD5_BYTES); + memcpy(&rawheader[60], header->parentmd5, CHD_MD5_BYTES); + put_bigendian_uint32(&rawheader[76], header->hunkbytes); + memcpy(&rawheader[80], header->sha1, CHD_SHA1_BYTES); + memcpy(&rawheader[100], header->parentsha1, CHD_SHA1_BYTES); + + /* seek and write */ + core_fseek(file, 0, SEEK_SET); + count = core_fwrite(file, rawheader, CHD_V3_HEADER_SIZE); + if (count != CHD_V3_HEADER_SIZE) + return CHDERR_WRITE_ERROR; + + return CHDERR_NONE; +} + + + +/*************************************************************************** + INTERNAL HUNK READ/WRITE +***************************************************************************/ + +/*------------------------------------------------- + hunk_read_into_cache - read a hunk into + the CHD's hunk cache +-------------------------------------------------*/ + +static chd_error hunk_read_into_cache(chd_file *chd, UINT32 hunknum) +{ + chd_error err; + + /* track the max */ + if (hunknum > chd->maxhunk) + chd->maxhunk = hunknum; + + /* if we're already in the cache, we're done */ + if (chd->cachehunk == hunknum) + return CHDERR_NONE; + chd->cachehunk = ~0; + + /* otherwise, read the data */ + err = hunk_read_into_memory(chd, hunknum, chd->cache); + if (err != CHDERR_NONE) + return err; + + /* mark the hunk successfully cached in */ + chd->cachehunk = hunknum; + return CHDERR_NONE; +} + + +/*------------------------------------------------- + hunk_read_into_memory - read a hunk into + memory at the given location +-------------------------------------------------*/ + +static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *dest) +{ + map_entry *entry = &chd->map[hunknum]; + chd_error err; + UINT32 bytes; + + /* return an error if out of range */ + if (hunknum >= chd->header.totalhunks) + return CHDERR_HUNK_OUT_OF_RANGE; + + /* switch off the entry type */ + switch (entry->flags & MAP_ENTRY_FLAG_TYPE_MASK) + { + /* compressed data */ + case MAP_ENTRY_TYPE_COMPRESSED: + + /* read it into the decompression buffer */ + core_fseek(chd->file, entry->offset, SEEK_SET); + bytes = core_fread(chd->file, chd->compressed, entry->length); + if (bytes != entry->length) + return CHDERR_READ_ERROR; + + /* now decompress using the codec */ + err = CHDERR_NONE; + if (chd->codecintf->decompress != NULL) + err = (*chd->codecintf->decompress)(chd, entry->length, dest); + if (err != CHDERR_NONE) + return err; + break; + + /* uncompressed data */ + case MAP_ENTRY_TYPE_UNCOMPRESSED: + core_fseek(chd->file, entry->offset, SEEK_SET); + bytes = core_fread(chd->file, dest, chd->header.hunkbytes); + if (bytes != chd->header.hunkbytes) + return CHDERR_READ_ERROR; + break; + + /* mini-compressed data */ + case MAP_ENTRY_TYPE_MINI: + put_bigendian_uint64(&dest[0], entry->offset); + for (bytes = 8; bytes < chd->header.hunkbytes; bytes++) + dest[bytes] = dest[bytes - 8]; + break; + + /* self-referenced data */ + case MAP_ENTRY_TYPE_SELF_HUNK: + if (chd->cachehunk == entry->offset && dest == chd->cache) + break; + return hunk_read_into_memory(chd, entry->offset, dest); + + /* parent-referenced data */ + case MAP_ENTRY_TYPE_PARENT_HUNK: + err = hunk_read_into_memory(chd->parent, entry->offset, dest); + if (err != CHDERR_NONE) + return err; + break; + } + return CHDERR_NONE; +} + + +/*------------------------------------------------- + hunk_write_from_memory - write a hunk from + memory into a CHD +-------------------------------------------------*/ + +static chd_error hunk_write_from_memory(chd_file *chd, UINT32 hunknum, const UINT8 *src) +{ + map_entry *entry = &chd->map[hunknum]; + map_entry newentry; + UINT8 fileentry[MAP_ENTRY_SIZE]; + const void *data = src; + UINT32 bytes = 0, match; + chd_error err; + + /* track the max */ + if (hunknum > chd->maxhunk) + chd->maxhunk = hunknum; + + /* first compute the CRC of the original data */ + newentry.crc = crc32(0, &src[0], chd->header.hunkbytes); + + /* if we're not a lossy codec, compute the CRC and look for matches */ + if (!chd->codecintf->lossy) + { + /* some extra stuff for zlib+ compression */ + if (chd->header.compression >= CHDCOMPRESSION_ZLIB_PLUS) + { + /* see if we can mini-compress first */ + for (bytes = 8; bytes < chd->header.hunkbytes; bytes++) + if (src[bytes] != src[bytes - 8]) + break; + + /* if so, we don't need to write any data */ + if (bytes == chd->header.hunkbytes) + { + newentry.offset = get_bigendian_uint64(&src[0]); + newentry.length = 0; + newentry.flags = MAP_ENTRY_TYPE_MINI; + goto write_entry; + } + + /* otherwise, see if we can find a match in the current file */ + match = crcmap_find_hunk(chd, hunknum, newentry.crc, &src[0]); + if (match != NO_MATCH) + { + newentry.offset = match; + newentry.length = 0; + newentry.flags = MAP_ENTRY_TYPE_SELF_HUNK; + goto write_entry; + } + + /* if we have a parent, see if we can find a match in there */ + if (chd->header.flags & CHDFLAGS_HAS_PARENT) + { + match = crcmap_find_hunk(chd->parent, ~0, newentry.crc, &src[0]); + if (match != NO_MATCH) + { + newentry.offset = match; + newentry.length = 0; + newentry.flags = MAP_ENTRY_TYPE_PARENT_HUNK; + goto write_entry; + } + } + } + } + + /* now try compressing the data */ + err = CHDERR_COMPRESSION_ERROR; + if (chd->codecintf->compress != NULL) + err = (*chd->codecintf->compress)(chd, src, &bytes); + + /* if that worked, and we're lossy, decompress and CRC the result */ + if (err == CHDERR_NONE && chd->codecintf->lossy) + { + err = (*chd->codecintf->decompress)(chd, bytes, chd->cache); + if (err == CHDERR_NONE) + newentry.crc = crc32(0, chd->cache, chd->header.hunkbytes); + } + + /* if we succeeded in compressing the data, replace our data pointer and mark it so */ + if (err == CHDERR_NONE) + { + data = chd->compressed; + newentry.length = bytes; + newentry.flags = MAP_ENTRY_TYPE_COMPRESSED; + } + + /* otherwise, mark it uncompressed and use the original data */ + else + { + newentry.length = chd->header.hunkbytes; + newentry.flags = MAP_ENTRY_TYPE_UNCOMPRESSED; + } + + /* if the data doesn't fit into the previous entry, make a new one at the eof */ + newentry.offset = entry->offset; + if (newentry.offset == 0 || newentry.length > entry->length) + newentry.offset = core_fsize(chd->file); + + /* write the data */ + core_fseek(chd->file, newentry.offset, SEEK_SET); + bytes = core_fwrite(chd->file, data, newentry.length); + if (bytes != newentry.length) + return CHDERR_WRITE_ERROR; + + /* update the entry in memory */ +write_entry: + *entry = newentry; + + /* update the map on file */ + map_assemble(&fileentry[0], &chd->map[hunknum]); + core_fseek(chd->file, chd->header.length + hunknum * sizeof(fileentry), SEEK_SET); + bytes = core_fwrite(chd->file, &fileentry[0], sizeof(fileentry)); + if (bytes != sizeof(fileentry)) + return CHDERR_WRITE_ERROR; + + return CHDERR_NONE; +} + + + +/*************************************************************************** + INTERNAL MAP ACCESS +***************************************************************************/ + +/*------------------------------------------------- + map_write_initial - write an initial map to + a new CHD file +-------------------------------------------------*/ + +static chd_error map_write_initial(core_file *file, chd_file *parent, const chd_header *header) +{ + UINT8 blank_map_entries[MAP_STACK_ENTRIES * MAP_ENTRY_SIZE]; + int fullchunks, remainder, count, i, j; + map_entry mapentry; + UINT64 fileoffset; + + /* create a mini hunk of 0's */ + mapentry.offset = 0; + mapentry.crc = 0; + mapentry.length = 0; + mapentry.flags = MAP_ENTRY_TYPE_MINI | MAP_ENTRY_FLAG_NO_CRC; + for (i = 0; i < MAP_STACK_ENTRIES; i++) + map_assemble(&blank_map_entries[i * MAP_ENTRY_SIZE], &mapentry); + + /* prepare to write a blank hunk map immediately following */ + fileoffset = header->length; + fullchunks = header->totalhunks / MAP_STACK_ENTRIES; + remainder = header->totalhunks % MAP_STACK_ENTRIES; + + /* first write full chunks of blank entries */ + for (i = 0; i < fullchunks; i++) + { + /* parent drives need to be mapped through */ + if (parent != NULL) + for (j = 0; j < MAP_STACK_ENTRIES; j++) + { + mapentry.offset = i * MAP_STACK_ENTRIES + j; + mapentry.crc = parent->map[i * MAP_STACK_ENTRIES + j].crc; + mapentry.flags = MAP_ENTRY_TYPE_PARENT_HUNK; + map_assemble(&blank_map_entries[j * MAP_ENTRY_SIZE], &mapentry); + } + + /* write the chunks */ + core_fseek(file, fileoffset, SEEK_SET); + count = core_fwrite(file, blank_map_entries, sizeof(blank_map_entries)); + if (count != sizeof(blank_map_entries)) + return CHDERR_WRITE_ERROR; + fileoffset += sizeof(blank_map_entries); + } + + /* then write the remainder */ + if (remainder > 0) + { + /* parent drives need to be mapped through */ + if (parent != NULL) + for (j = 0; j < remainder; j++) + { + mapentry.offset = i * MAP_STACK_ENTRIES + j; + mapentry.crc = parent->map[i * MAP_STACK_ENTRIES + j].crc; + mapentry.flags = MAP_ENTRY_TYPE_PARENT_HUNK; + map_assemble(&blank_map_entries[j * MAP_ENTRY_SIZE], &mapentry); + } + + /* write the chunks */ + core_fseek(file, fileoffset, SEEK_SET); + count = core_fwrite(file, blank_map_entries, remainder * MAP_ENTRY_SIZE); + if (count != remainder * MAP_ENTRY_SIZE) + return CHDERR_WRITE_ERROR; + fileoffset += remainder * MAP_ENTRY_SIZE; + } + + /* then write a special end-of-list cookie */ + memcpy(&blank_map_entries[0], END_OF_LIST_COOKIE, MAP_ENTRY_SIZE); + core_fseek(file, fileoffset, SEEK_SET); + count = core_fwrite(file, blank_map_entries, MAP_ENTRY_SIZE); + if (count != MAP_ENTRY_SIZE) + return CHDERR_WRITE_ERROR; + + return CHDERR_NONE; +} + + +/*------------------------------------------------- + map_read - read the initial sector map +-------------------------------------------------*/ + +static chd_error map_read(chd_file *chd) +{ + UINT32 entrysize = (chd->header.version < 3) ? OLD_MAP_ENTRY_SIZE : MAP_ENTRY_SIZE; + UINT8 raw_map_entries[MAP_STACK_ENTRIES * MAP_ENTRY_SIZE]; + UINT64 fileoffset, maxoffset = 0; + UINT8 cookie[MAP_ENTRY_SIZE]; + UINT32 count; + int i, err; + + /* first allocate memory */ + chd->map = malloc(sizeof(chd->map[0]) * chd->header.totalhunks); + if (!chd->map) + return CHDERR_OUT_OF_MEMORY; + + /* read the map entries in in chunks and extract to the map list */ + fileoffset = chd->header.length; + for (i = 0; i < chd->header.totalhunks; i += MAP_STACK_ENTRIES) + { + /* compute how many entries this time */ + int entries = chd->header.totalhunks - i, j; + if (entries > MAP_STACK_ENTRIES) + entries = MAP_STACK_ENTRIES; + + /* read that many */ + core_fseek(chd->file, fileoffset, SEEK_SET); + count = core_fread(chd->file, raw_map_entries, entries * entrysize); + if (count != entries * entrysize) + { + err = CHDERR_READ_ERROR; + goto cleanup; + } + fileoffset += entries * entrysize; + + /* process that many */ + if (entrysize == MAP_ENTRY_SIZE) + { + for (j = 0; j < entries; j++) + map_extract(&raw_map_entries[j * MAP_ENTRY_SIZE], &chd->map[i + j]); + } + else + { + for (j = 0; j < entries; j++) + map_extract_old(&raw_map_entries[j * OLD_MAP_ENTRY_SIZE], &chd->map[i + j], chd->header.hunkbytes); + } + + /* track the maximum offset */ + for (j = 0; j < entries; j++) + if ((chd->map[i + j].flags & MAP_ENTRY_FLAG_TYPE_MASK) == MAP_ENTRY_TYPE_COMPRESSED || + (chd->map[i + j].flags & MAP_ENTRY_FLAG_TYPE_MASK) == MAP_ENTRY_TYPE_UNCOMPRESSED) + maxoffset = MAX(maxoffset, chd->map[i + j].offset + chd->map[i + j].length); + } + + /* verify the cookie */ + core_fseek(chd->file, fileoffset, SEEK_SET); + count = core_fread(chd->file, &cookie, entrysize); + if (count != entrysize || memcmp(&cookie, END_OF_LIST_COOKIE, entrysize)) + { + err = CHDERR_INVALID_FILE; + goto cleanup; + } + + /* verify the length */ + if (maxoffset > core_fsize(chd->file)) + { + err = CHDERR_INVALID_FILE; + goto cleanup; + } + return CHDERR_NONE; + +cleanup: + if (chd->map) + free(chd->map); + chd->map = NULL; + return err; +} + + + +/*************************************************************************** + INTERNAL CRC MAP ACCESS +***************************************************************************/ + +/*------------------------------------------------- + crcmap_init - initialize the CRC map +-------------------------------------------------*/ + +static void crcmap_init(chd_file *chd, int prepopulate) +{ + int i; + + /* if we already have one, bail */ + if (chd->crcmap != NULL) + return; + + /* reset all pointers */ + chd->crcmap = NULL; + chd->crcfree = NULL; + chd->crctable = NULL; + + /* allocate a list; one for each hunk */ + chd->crcmap = malloc(chd->header.totalhunks * sizeof(chd->crcmap[0])); + if (chd->crcmap == NULL) + return; + + /* allocate a CRC map table */ + chd->crctable = malloc(CRCMAP_HASH_SIZE * sizeof(chd->crctable[0])); + if (chd->crctable == NULL) + { + free(chd->crcmap); + chd->crcmap = NULL; + return; + } + + /* initialize the free list */ + for (i = 0; i < chd->header.totalhunks; i++) + { + chd->crcmap[i].next = chd->crcfree; + chd->crcfree = &chd->crcmap[i]; + } + + /* initialize the table */ + memset(chd->crctable, 0, CRCMAP_HASH_SIZE * sizeof(chd->crctable[0])); + + /* if we're to prepopulate, go for it */ + if (prepopulate) + for (i = 0; i < chd->header.totalhunks; i++) + crcmap_add_entry(chd, i); +} + + +/*------------------------------------------------- + crcmap_add_entry - log a CRC entry +-------------------------------------------------*/ + +static void crcmap_add_entry(chd_file *chd, UINT32 hunknum) +{ + UINT32 hash = chd->map[hunknum].crc % CRCMAP_HASH_SIZE; + crcmap_entry *crcmap; + + /* pull a free entry off the list */ + crcmap = chd->crcfree; + chd->crcfree = crcmap->next; + + /* set up the entry and link it into the hash table */ + crcmap->hunknum = hunknum; + crcmap->next = chd->crctable[hash]; + chd->crctable[hash] = crcmap; +} + + +/*------------------------------------------------- + crcmap_verify_hunk_match - verify that a + hunk really matches by doing a byte-for-byte + compare +-------------------------------------------------*/ + +static int crcmap_verify_hunk_match(chd_file *chd, UINT32 hunknum, const UINT8 *rawdata) +{ + /* we have a potential match -- better be sure */ + /* read the hunk from disk and compare byte-for-byte */ + if (hunknum != chd->comparehunk) + { + chd->comparehunk = ~0; + if (hunk_read_into_memory(chd, hunknum, chd->compare) == CHDERR_NONE) + chd->comparehunk = hunknum; + } + return (hunknum == chd->comparehunk && memcmp(rawdata, chd->compare, chd->header.hunkbytes) == 0); +} + + +/*------------------------------------------------- + crcmap_find_hunk - find a hunk with a matching + CRC in the map +-------------------------------------------------*/ + +static UINT32 crcmap_find_hunk(chd_file *chd, UINT32 hunknum, UINT32 crc, const UINT8 *rawdata) +{ + UINT32 lasthunk = (hunknum < chd->header.totalhunks) ? hunknum : chd->header.totalhunks; + int curhunk; + + /* if we have a CRC map, use that */ + if (chd->crctable) + { + crcmap_entry *curentry; + for (curentry = chd->crctable[crc % CRCMAP_HASH_SIZE]; curentry; curentry = curentry->next) + { + curhunk = curentry->hunknum; + if (chd->map[curhunk].crc == crc && !(chd->map[curhunk].flags & MAP_ENTRY_FLAG_NO_CRC) && crcmap_verify_hunk_match(chd, curhunk, rawdata)) + return curhunk; + } + return NO_MATCH; + } + + /* first see if the last match is a valid one */ + if (chd->comparehunk < chd->header.totalhunks && chd->map[chd->comparehunk].crc == crc && !(chd->map[chd->comparehunk].flags & MAP_ENTRY_FLAG_NO_CRC) && + memcmp(rawdata, chd->compare, chd->header.hunkbytes) == 0) + return chd->comparehunk; + + /* scan through the CHD's hunk map looking for a match */ + for (curhunk = 0; curhunk < lasthunk; curhunk++) + if (chd->map[curhunk].crc == crc && !(chd->map[curhunk].flags & MAP_ENTRY_FLAG_NO_CRC) && crcmap_verify_hunk_match(chd, curhunk, rawdata)) + return curhunk; + + return NO_MATCH; +} + + + +/*************************************************************************** + INTERNAL METADATA ACCESS +***************************************************************************/ + +/*------------------------------------------------- + metadata_find_entry - find a metadata entry +-------------------------------------------------*/ + +static chd_error metadata_find_entry(chd_file *chd, UINT32 metatag, UINT32 metaindex, metadata_entry *metaentry) +{ + /* start at the beginning */ + metaentry->offset = chd->header.metaoffset; + metaentry->prev = 0; + + /* loop until we run out of options */ + while (metaentry->offset != 0) + { + UINT8 raw_meta_header[METADATA_HEADER_SIZE]; + UINT32 count; + + /* read the raw header */ + core_fseek(chd->file, metaentry->offset, SEEK_SET); + count = core_fread(chd->file, raw_meta_header, sizeof(raw_meta_header)); + if (count != sizeof(raw_meta_header)) + break; + + /* extract the data */ + metaentry->metatag = get_bigendian_uint32(&raw_meta_header[0]); + metaentry->length = get_bigendian_uint32(&raw_meta_header[4]); + metaentry->next = get_bigendian_uint64(&raw_meta_header[8]); + + /* if we got a match, proceed */ + if (metatag == CHDMETATAG_WILDCARD || metaentry->metatag == metatag) + if (metaindex-- == 0) + return CHDERR_NONE; + + /* no match, fetch the next link */ + metaentry->prev = metaentry->offset; + metaentry->offset = metaentry->next; + } + + /* if we get here, we didn't find it */ + return CHDERR_METADATA_NOT_FOUND; +} + + +/*------------------------------------------------- + metadata_set_previous_next - set the 'next' + offset of a piece of metadata +-------------------------------------------------*/ + +static chd_error metadata_set_previous_next(chd_file *chd, UINT64 prevoffset, UINT64 nextoffset) +{ + UINT8 raw_meta_header[METADATA_HEADER_SIZE]; + chd_error err; + UINT32 count; + + /* if we were the first entry, make the next entry the first */ + if (prevoffset == 0) + { + chd->header.metaoffset = nextoffset; + err = header_write(chd->file, &chd->header); + if (err != CHDERR_NONE) + return err; + } + + /* otherwise, update the link in the previous pointer */ + else + { + /* read the previous raw header */ + core_fseek(chd->file, prevoffset, SEEK_SET); + count = core_fread(chd->file, raw_meta_header, sizeof(raw_meta_header)); + if (count != sizeof(raw_meta_header)) + return CHDERR_READ_ERROR; + + /* copy our next pointer into the previous->next offset */ + put_bigendian_uint64(&raw_meta_header[8], nextoffset); + + /* write the previous raw header */ + core_fseek(chd->file, prevoffset, SEEK_SET); + count = core_fwrite(chd->file, raw_meta_header, sizeof(raw_meta_header)); + if (count != sizeof(raw_meta_header)) + return CHDERR_WRITE_ERROR; + } + + return CHDERR_NONE; +} + + +/*------------------------------------------------- + metadata_set_length - set the length field of + a piece of metadata +-------------------------------------------------*/ + +static chd_error metadata_set_length(chd_file *chd, UINT64 offset, UINT32 length) +{ + UINT8 raw_meta_header[METADATA_HEADER_SIZE]; + UINT32 count; + + /* read the raw header */ + core_fseek(chd->file, offset, SEEK_SET); + count = core_fread(chd->file, raw_meta_header, sizeof(raw_meta_header)); + if (count != sizeof(raw_meta_header)) + return CHDERR_READ_ERROR; + + /* update the length at offset 4 */ + put_bigendian_uint32(&raw_meta_header[4], length); + + /* write the raw header */ + core_fseek(chd->file, offset, SEEK_SET); + count = core_fwrite(chd->file, raw_meta_header, sizeof(raw_meta_header)); + if (count != sizeof(raw_meta_header)) + return CHDERR_WRITE_ERROR; + + return CHDERR_NONE; +} + + + +/*************************************************************************** + ZLIB COMPRESSION CODEC +***************************************************************************/ + +/*------------------------------------------------- + zlib_codec_init - initialize the ZLIB codec +-------------------------------------------------*/ + +static chd_error zlib_codec_init(chd_file *chd) +{ + zlib_codec_data *data; + chd_error err; + int zerr; + + /* allocate memory for the 2 stream buffers */ + data = malloc(sizeof(*data)); + if (data == NULL) + return CHDERR_OUT_OF_MEMORY; + + /* clear the buffers */ + memset(data, 0, sizeof(*data)); + + /* init the inflater first */ + data->inflater.next_in = (Bytef *)data; /* bogus, but that's ok */ + data->inflater.avail_in = 0; + data->inflater.zalloc = zlib_fast_alloc; + data->inflater.zfree = zlib_fast_free; + data->inflater.opaque = data; + zerr = inflateInit2(&data->inflater, -MAX_WBITS); + + /* if that worked, initialize the deflater */ + if (zerr == Z_OK) + { + data->deflater.next_in = (Bytef *)data; /* bogus, but that's ok */ + data->deflater.avail_in = 0; + data->deflater.zalloc = zlib_fast_alloc; + data->deflater.zfree = zlib_fast_free; + data->deflater.opaque = data; + zerr = deflateInit2(&data->deflater, Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS, 8, Z_DEFAULT_STRATEGY); + } + + /* convert errors */ + if (zerr == Z_MEM_ERROR) + err = CHDERR_OUT_OF_MEMORY; + else if (zerr != Z_OK) + err = CHDERR_CODEC_ERROR; + else + err = CHDERR_NONE; + + /* handle an error */ + if (err == CHDERR_NONE) + chd->codecdata = data; + else + free(data); + + return err; +} + + +/*------------------------------------------------- + zlib_codec_free - free data for the ZLIB + codec +-------------------------------------------------*/ + +static void zlib_codec_free(chd_file *chd) +{ + zlib_codec_data *data = chd->codecdata; + + /* deinit the streams */ + if (data != NULL) + { + int i; + + inflateEnd(&data->inflater); + deflateEnd(&data->deflater); + + /* free our fast memory */ + for (i = 0; i < MAX_ZLIB_ALLOCS; i++) + if (data->allocptr[i]) + free(data->allocptr[i]); + free(data); + } +} + + +/*------------------------------------------------- + zlib_codec_compress - compress data using the + ZLIB codec +-------------------------------------------------*/ + +static chd_error zlib_codec_compress(chd_file *chd, const void *src, UINT32 *length) +{ + zlib_codec_data *data = chd->codecdata; + int zerr; + + /* reset the decompressor */ + data->deflater.next_in = (void *)src; + data->deflater.avail_in = chd->header.hunkbytes; + data->deflater.total_in = 0; + data->deflater.next_out = chd->compressed; + data->deflater.avail_out = chd->header.hunkbytes; + data->deflater.total_out = 0; + zerr = deflateReset(&data->deflater); + if (zerr != Z_OK) + return CHDERR_COMPRESSION_ERROR; + + /* do it */ + zerr = deflate(&data->deflater, Z_FINISH); + + /* if we ended up with more data than we started with, return an error */ + if (zerr != Z_STREAM_END || data->deflater.total_out >= chd->header.hunkbytes) + return CHDERR_COMPRESSION_ERROR; + + /* otherwise, fill in the length and return success */ + *length = data->deflater.total_out; + return CHDERR_NONE; +} + + +/*------------------------------------------------- + zlib_codec_decompress - decomrpess data using + the ZLIB codec +-------------------------------------------------*/ + +static chd_error zlib_codec_decompress(chd_file *chd, UINT32 srclength, void *dest) +{ + zlib_codec_data *data = chd->codecdata; + int zerr; + + /* reset the decompressor */ + data->inflater.next_in = chd->compressed; + data->inflater.avail_in = srclength; + data->inflater.total_in = 0; + data->inflater.next_out = dest; + data->inflater.avail_out = chd->header.hunkbytes; + data->inflater.total_out = 0; + zerr = inflateReset(&data->inflater); + if (zerr != Z_OK) + return CHDERR_DECOMPRESSION_ERROR; + + /* do it */ + zerr = inflate(&data->inflater, Z_FINISH); + if (data->inflater.total_out != chd->header.hunkbytes) + return CHDERR_DECOMPRESSION_ERROR; + + return CHDERR_NONE; +} + + +/*------------------------------------------------- + zlib_fast_alloc - fast malloc for ZLIB, which + allocates and frees memory frequently +-------------------------------------------------*/ + +static voidpf zlib_fast_alloc(voidpf opaque, uInt items, uInt size) +{ + zlib_codec_data *data = opaque; + UINT32 *ptr; + int i; + + /* compute the size, rounding to the nearest 1k */ + size = (size * items + 0x3ff) & ~0x3ff; + + /* reuse a hunk if we can */ + for (i = 0; i < MAX_ZLIB_ALLOCS; i++) + { + ptr = data->allocptr[i]; + if (ptr && size == *ptr) + { + /* set the low bit of the size so we don't match next time */ + *ptr |= 1; + return ptr + 1; + } + } + + /* alloc a new one */ + ptr = malloc(size + sizeof(UINT32)); + if (!ptr) + return NULL; + + /* put it into the list */ + for (i = 0; i < MAX_ZLIB_ALLOCS; i++) + if (!data->allocptr[i]) + { + data->allocptr[i] = ptr; + break; + } + + /* set the low bit of the size so we don't match next time */ + *ptr = size | 1; + return ptr + 1; +} + + +/*------------------------------------------------- + zlib_fast_free - fast free for ZLIB, which + allocates and frees memory frequently +-------------------------------------------------*/ + +static void zlib_fast_free(voidpf opaque, voidpf address) +{ + zlib_codec_data *data = opaque; + UINT32 *ptr = (UINT32 *)address - 1; + int i; + + /* find the hunk */ + for (i = 0; i < MAX_ZLIB_ALLOCS; i++) + if (ptr == data->allocptr[i]) + { + /* clear the low bit of the size to allow matches */ + *ptr &= ~1; + return; + } +} + + + +/*************************************************************************** + AV COMPRESSION CODEC +***************************************************************************/ + +/*------------------------------------------------- + av_raw_data_size - compute the raw data size +-------------------------------------------------*/ + +INLINE UINT32 av_raw_data_size(const UINT8 *data) +{ + int size = 0; + + /* make sure we have a correct header */ + if (data[0] == 'c' && data[1] == 'h' && data[2] == 'a' && data[3] == 'v') + { + /* add in header size plus metadata length */ + size = 12 + data[4]; + + /* add in channels * samples */ + size += 2 * data[5] * ((data[6] << 8) + data[7]); + + /* add in 2 * width * height */ + size += 2 * ((data[8] << 8) + data[9]) * (((data[10] << 8) + data[11]) & 0x7fff); + } + return size; +} + + +/*------------------------------------------------- + av_codec_init - initialize the A/V codec +-------------------------------------------------*/ + +static chd_error av_codec_init(chd_file *chd) +{ + av_codec_data *data; + + /* allocate memory for the 2 stream buffers */ + data = malloc(sizeof(*data)); + if (data == NULL) + return CHDERR_OUT_OF_MEMORY; + + /* clear the buffers */ + memset(data, 0, sizeof(*data)); + chd->codecdata = data; + + /* set up the default decompression configuration */ + data->decompconfig.decode_mask = AVCOMP_DECODE_DEFAULT; + + /* attempt to do a post-init now; if we're creating a new CHD, this won't work */ + /* but that's ok */ + av_codec_postinit(chd); + return CHDERR_NONE; +} + + +/*------------------------------------------------- + av_codec_free - free data for the A/V + codec +-------------------------------------------------*/ + +static void av_codec_free(chd_file *chd) +{ + av_codec_data *data = chd->codecdata; + + /* deinit avcomp */ + if (data != NULL) + { + if (data->compstate != NULL) + avcomp_free(data->compstate); + free(data); + } +} + + +/*------------------------------------------------- + av_codec_compress - compress data using the + A/V codec +-------------------------------------------------*/ + +static chd_error av_codec_compress(chd_file *chd, const void *src, UINT32 *length) +{ + av_codec_data *data = chd->codecdata; + int averr; + int size; + + /* if we haven't yet set up the avcomp code, do it now */ + if (data->compstate == NULL) + { + chd_error chderr = av_codec_postinit(chd); + if (chderr != CHDERR_NONE) + return chderr; + } + + /* make sure short frames are padded with 0 */ + size = av_raw_data_size(src); + while (size < chd->header.hunkbytes) + if (((const UINT8 *)src)[size++] != 0) + return CHDERR_INVALID_DATA; + + /* encode the audio and video */ + averr = avcomp_encode_data(data->compstate, src, chd->compressed, length); + if (averr != AVCERR_NONE || *length > chd->header.hunkbytes) + return CHDERR_COMPRESSION_ERROR; + + return CHDERR_NONE; +} + + +/*------------------------------------------------- + av_codec_decompress - decomrpess data using + the A/V codec +-------------------------------------------------*/ + +static chd_error av_codec_decompress(chd_file *chd, UINT32 srclength, void *dest) +{ + av_codec_data *data = chd->codecdata; + const UINT8 *source; + avcomp_error averr; + int size; + + /* if we haven't yet set up the avcomp code, do it now */ + if (data->compstate == NULL) + { + chd_error chderr = av_codec_postinit(chd); + if (chderr != CHDERR_NONE) + return chderr; + } + + /* decode the audio and video */ + source = chd->compressed; + averr = avcomp_decode_data(data->compstate, source, srclength, dest); + if (averr != AVCERR_NONE) + return CHDERR_DECOMPRESSION_ERROR; + + /* pad short frames with 0 */ + size = av_raw_data_size(dest); + while (size < chd->header.hunkbytes) + ((UINT8 *)dest)[size++] = 0; + + return CHDERR_NONE; +} + + +/*------------------------------------------------- + av_codec_config - codec-specific configuration + for the A/V codec +-------------------------------------------------*/ + +static chd_error av_codec_config(chd_file *chd, int param, void *config) +{ + av_codec_data *data = chd->codecdata; + + /* if we're getting the decompression configuration, apply it now */ + if (param == AV_CODEC_DECOMPRESS_CONFIG) + { + data->decompconfig = *(av_codec_decompress_config *)config; + if (data->compstate != NULL) + avcomp_decompress_config(data->compstate, data->decompconfig.decode_mask, data->decompconfig.video_buffer, data->decompconfig.video_stride, data->decompconfig.video_xor, data->decompconfig.audio_xor); + return CHDERR_NONE; + } + + /* anything else is invalid */ + return CHDERR_INVALID_PARAMETER; +} + + +/*------------------------------------------------- + av_codec_postinit - actual initialization of + avcomp happens here, on the first attempt + to compress or decompress data +-------------------------------------------------*/ + +static chd_error av_codec_postinit(chd_file *chd) +{ + int fps, fpsfrac, width, height, interlaced, channels, rate, metabytes; + UINT32 fps_times_1million, max_samples_per_frame, bytes_per_frame; + av_codec_data *data = chd->codecdata; + char metadata[256]; + chd_error err; + + /* the code below won't work asynchronously */ + if (chd->workitem != NULL) + return CHDERR_OPERATION_PENDING; + + /* get the metadata */ + err = chd_get_metadata(chd, AV_METADATA_TAG, 0, metadata, sizeof(metadata), NULL, NULL); + if (err != CHDERR_NONE) + return err; + + /* extract the info */ + if (sscanf(metadata, AV_METADATA_FORMAT, &fps, &fpsfrac, &width, &height, &interlaced, &channels, &rate, &metabytes) != 8) + return CHDERR_INVALID_METADATA; + + /* compute the bytes per frame */ + fps_times_1million = fps * 1000000 + fpsfrac; + max_samples_per_frame = ((UINT64)rate * 1000000 + fps_times_1million - 1) / fps_times_1million; + bytes_per_frame = 12 + metabytes + channels * max_samples_per_frame * 2 + width * height * 2; + if (bytes_per_frame > chd->header.hunkbytes) + return CHDERR_INVALID_METADATA; + + /* create the avcomp state */ + data->compstate = avcomp_init(width, height, channels); + + /* configure the codec */ + avcomp_decompress_config(data->compstate, data->decompconfig.decode_mask, data->decompconfig.video_buffer, data->decompconfig.video_stride, data->decompconfig.video_xor, data->decompconfig.audio_xor); + return CHDERR_NONE; +} diff --git a/src/lib/util/chd.h b/src/lib/util/chd.h new file mode 100644 index 00000000000..dd549cd76ca --- /dev/null +++ b/src/lib/util/chd.h @@ -0,0 +1,325 @@ +/*************************************************************************** + + MAME Compressed Hunks of Data file format + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#pragma once + +#ifndef __CHD_H__ +#define __CHD_H__ + +#include "osdcore.h" +#include "corefile.h" + + +/*************************************************************************** + + Compressed Hunks of Data header format. All numbers are stored in + Motorola (big-endian) byte ordering. The header is 76 (V1) or 80 (V2) + bytes long. + + V1 header: + + [ 0] char tag[8]; // 'MComprHD' + [ 8] UINT32 length; // length of header (including tag and length fields) + [ 12] UINT32 version; // drive format version + [ 16] UINT32 flags; // flags (see below) + [ 20] UINT32 compression; // compression type + [ 24] UINT32 hunksize; // 512-byte sectors per hunk + [ 28] UINT32 totalhunks; // total # of hunks represented + [ 32] UINT32 cylinders; // number of cylinders on hard disk + [ 36] UINT32 heads; // number of heads on hard disk + [ 40] UINT32 sectors; // number of sectors on hard disk + [ 44] UINT8 md5[16]; // MD5 checksum of raw data + [ 60] UINT8 parentmd5[16]; // MD5 checksum of parent file + [ 76] (V1 header length) + + V2 header: + + [ 0] char tag[8]; // 'MComprHD' + [ 8] UINT32 length; // length of header (including tag and length fields) + [ 12] UINT32 version; // drive format version + [ 16] UINT32 flags; // flags (see below) + [ 20] UINT32 compression; // compression type + [ 24] UINT32 hunksize; // seclen-byte sectors per hunk + [ 28] UINT32 totalhunks; // total # of hunks represented + [ 32] UINT32 cylinders; // number of cylinders on hard disk + [ 36] UINT32 heads; // number of heads on hard disk + [ 40] UINT32 sectors; // number of sectors on hard disk + [ 44] UINT8 md5[16]; // MD5 checksum of raw data + [ 60] UINT8 parentmd5[16]; // MD5 checksum of parent file + [ 76] UINT32 seclen; // number of bytes per sector + [ 80] (V2 header length) + + V3 header: + + [ 0] char tag[8]; // 'MComprHD' + [ 8] UINT32 length; // length of header (including tag and length fields) + [ 12] UINT32 version; // drive format version + [ 16] UINT32 flags; // flags (see below) + [ 20] UINT32 compression; // compression type + [ 24] UINT32 totalhunks; // total # of hunks represented + [ 28] UINT64 logicalbytes; // logical size of the data (in bytes) + [ 36] UINT64 metaoffset; // offset to the first blob of metadata + [ 44] UINT8 md5[16]; // MD5 checksum of raw data + [ 60] UINT8 parentmd5[16]; // MD5 checksum of parent file + [ 76] UINT32 hunkbytes; // number of bytes per hunk + [ 80] UINT8 sha1[20]; // SHA1 checksum of raw data + [100] UINT8 parentsha1[20];// SHA1 checksum of parent file + [120] (V3 header length) + + Flags: + 0x00000001 - set if this drive has a parent + 0x00000002 - set if this drive allows writes + +***************************************************************************/ + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +/* header information */ +#define CHD_HEADER_VERSION 3 +#define CHD_V1_HEADER_SIZE 76 +#define CHD_V2_HEADER_SIZE 80 +#define CHD_V3_HEADER_SIZE 120 +#define CHD_MAX_HEADER_SIZE CHD_V3_HEADER_SIZE + +/* checksumming information */ +#define CHD_MD5_BYTES 16 +#define CHD_SHA1_BYTES 20 + +/* CHD global flags */ +#define CHDFLAGS_HAS_PARENT 0x00000001 +#define CHDFLAGS_IS_WRITEABLE 0x00000002 +#define CHDFLAGS_UNDEFINED 0xfffffffc + +/* compression types */ +#define CHDCOMPRESSION_NONE 0 +#define CHDCOMPRESSION_ZLIB 1 +#define CHDCOMPRESSION_ZLIB_PLUS 2 +#define CHDCOMPRESSION_AV 3 + +/* A/V codec configuration parameters */ +#define AV_CODEC_COMPRESS_CONFIG 0 +#define AV_CODEC_DECOMPRESS_CONFIG 1 + +/* metadata parameters */ +#define CHDMETATAG_WILDCARD 0 +#define CHD_METAINDEX_APPEND ((UINT32)-1) + +/* standard hard disk metadata */ +#define HARD_DISK_METADATA_TAG 0x47444444 /* 'GDDD' */ +#define HARD_DISK_METADATA_FORMAT "CYLS:%d,HEADS:%d,SECS:%d,BPS:%d" + +/* standard CD-ROM metadata */ +#define CDROM_OLD_METADATA_TAG 0x43484344 /* 'CHCD' */ +#define CDROM_TRACK_METADATA_TAG 0x43485452 /* 'CHTR' */ +#define CDROM_TRACK_METADATA_FORMAT "TRACK:%d TYPE:%s SUBTYPE:%s FRAMES:%d" + +/* standard A/V metadata */ +#define AV_METADATA_TAG 0x41564156 /* 'AVAV' */ +#define AV_METADATA_FORMAT "FPS:%d.%06d WIDTH:%d HEIGHT:%d INTERLACED:%d CHANNELS:%d SAMPLERATE:%d META:%d" + +/* CHD open values */ +#define CHD_OPEN_READ 1 +#define CHD_OPEN_READWRITE 2 + +/* error types */ +enum _chd_error +{ + CHDERR_NONE, + CHDERR_NO_INTERFACE, + CHDERR_OUT_OF_MEMORY, + CHDERR_INVALID_FILE, + CHDERR_INVALID_PARAMETER, + CHDERR_INVALID_DATA, + CHDERR_FILE_NOT_FOUND, + CHDERR_REQUIRES_PARENT, + CHDERR_FILE_NOT_WRITEABLE, + CHDERR_READ_ERROR, + CHDERR_WRITE_ERROR, + CHDERR_CODEC_ERROR, + CHDERR_INVALID_PARENT, + CHDERR_HUNK_OUT_OF_RANGE, + CHDERR_DECOMPRESSION_ERROR, + CHDERR_COMPRESSION_ERROR, + CHDERR_CANT_CREATE_FILE, + CHDERR_CANT_VERIFY, + CHDERR_NOT_SUPPORTED, + CHDERR_METADATA_NOT_FOUND, + CHDERR_INVALID_METADATA_SIZE, + CHDERR_UNSUPPORTED_VERSION, + CHDERR_VERIFY_INCOMPLETE, + CHDERR_INVALID_METADATA, + CHDERR_INVALID_STATE, + CHDERR_OPERATION_PENDING, + CHDERR_NO_ASYNC_OPERATION, + CHDERR_UNSUPPORTED_FORMAT +}; +typedef enum _chd_error chd_error; + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* opaque types */ +typedef struct _chd_file chd_file; + + +/* progress callback function */ +typedef void (*chd_progress_printf)(const char *format, ...); + + +/* extract header structure (NOT the on-disk header structure) */ +typedef struct _chd_header chd_header; +struct _chd_header +{ + UINT32 length; /* length of header data */ + UINT32 version; /* drive format version */ + UINT32 flags; /* flags field */ + UINT32 compression; /* compression type */ + UINT32 hunkbytes; /* number of bytes per hunk */ + UINT32 totalhunks; /* total # of hunks represented */ + UINT64 logicalbytes; /* logical size of the data */ + UINT64 metaoffset; /* offset in file of first metadata */ + UINT8 md5[CHD_MD5_BYTES]; /* MD5 checksum of raw data */ + UINT8 parentmd5[CHD_MD5_BYTES]; /* MD5 checksum of parent file */ + UINT8 sha1[CHD_SHA1_BYTES]; /* SHA1 checksum of raw data */ + UINT8 parentsha1[CHD_SHA1_BYTES]; /* SHA1 checksum of parent file */ + + UINT32 obsolete_cylinders; /* obsolete field -- do not use! */ + UINT32 obsolete_sectors; /* obsolete field -- do not use! */ + UINT32 obsolete_heads; /* obsolete field -- do not use! */ + UINT32 obsolete_hunksize; /* obsolete field -- do not use! */ +}; + + +/* A/V codec decompression configuration */ +typedef struct _av_codec_decompress_config av_codec_decompress_config; +struct _av_codec_decompress_config +{ + UINT32 decode_mask; /* decoding mask */ + UINT8 * video_buffer; /* pointer to alternate video buffer */ + UINT32 video_stride; /* bytes between rows in video buffer */ + UINT32 video_xor; /* XOR to apply to bytes in video buffer */ + UINT32 audio_xor; /* XOR to apply to bytes in audio buffer */ +}; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + + +/* ----- CHD file management ----- */ + +/* create a new CHD file fitting the given description */ +chd_error chd_create(const char *filename, UINT64 logicalbytes, UINT32 hunkbytes, UINT32 compression, chd_file *parent); + +/* same as chd_create(), but accepts an already-opened core_file object */ +chd_error chd_create_file(core_file *file, UINT64 logicalbytes, UINT32 hunkbytes, UINT32 compression, chd_file *parent); + +/* open an existing CHD file */ +chd_error chd_open(const char *filename, int mode, chd_file *parent, chd_file **chd); + +/* same as chd_open(), but accepts an already-opened core_file object */ +chd_error chd_open_file(core_file *file, int mode, chd_file *parent, chd_file **chd); + +/* close a CHD file */ +void chd_close(chd_file *chd); + +/* return the associated core_file */ +core_file *chd_core_file(chd_file *chd); + + + +/* ----- CHD header management ----- */ + +/* return a pointer to the extracted CHD header data */ +const chd_header *chd_get_header(chd_file *chd); + +/* set a modified header */ +chd_error chd_set_header(const char *filename, const chd_header *header); + +/* same as chd_set_header(), but accepts an already-opened core_file object */ +chd_error chd_set_header_file(core_file *file, const chd_header *header); + + + +/* ----- core data read/write ----- */ + +/* read one hunk from the CHD file */ +chd_error chd_read(chd_file *chd, UINT32 hunknum, void *buffer); + +/* read one hunk from the CHD file asynchronously */ +chd_error chd_read_async(chd_file *chd, UINT32 hunknum, void *buffer); + +/* write one hunk to a CHD file */ +chd_error chd_write(chd_file *chd, UINT32 hunknum, const void *buffer); + +/* write one hunk to a CHD file asynchronously */ +chd_error chd_write_async(chd_file *chd, UINT32 hunknum, const void *buffer); + +/* wait for a previously issued async read/write to complete and return the error */ +chd_error chd_async_complete(chd_file *chd); + + + +/* ----- metadata management ----- */ + +/* get indexed metadata of a particular sort */ +chd_error chd_get_metadata(chd_file *chd, UINT32 searchtag, UINT32 searchindex, void *output, UINT32 outputlen, UINT32 *resultlen, UINT32 *resulttag); + +/* set indexed metadata of a particular sort */ +chd_error chd_set_metadata(chd_file *chd, UINT32 metatag, UINT32 metaindex, const void *inputbuf, UINT32 inputlen); + +/* clone all of the metadata from one CHD to another */ +chd_error chd_clone_metadata(chd_file *source, chd_file *dest); + + + +/* ----- compression management ----- */ + +/* begin compressing data to a CHD */ +chd_error chd_compress_begin(chd_file *chd); + +/* compress the next hunk of data */ +chd_error chd_compress_hunk(chd_file *chd, const void *data, double *curratio); + +/* finish compressing data to a CHD */ +chd_error chd_compress_finish(chd_file *chd); + + + +/* ----- verification management ----- */ + +/* begin verifying a CHD */ +chd_error chd_verify_begin(chd_file *chd); + +/* verify a single hunk of data */ +chd_error chd_verify_hunk(chd_file *chd); + +/* finish verifying a CHD, returning the computed MD5 and SHA1 */ +chd_error chd_verify_finish(chd_file *chd, UINT8 *finalmd5, UINT8 *finalsha1); + + + +/* ----- codec interfaces ----- */ + +/* set internal codec parameters */ +chd_error chd_codec_config(chd_file *chd, int param, void *config); + +/* return a string description of a codec */ +const char *chd_get_codec_name(UINT32 codec); + + +#endif /* __CHD_H__ */ diff --git a/src/lib/util/corefile.c b/src/lib/util/corefile.c new file mode 100644 index 00000000000..008e919a8f7 --- /dev/null +++ b/src/lib/util/corefile.c @@ -0,0 +1,726 @@ +/*************************************************************************** + + corefile.c + + File access functions. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#include "corefile.h" +#include "unicode.h" + +#include <stdarg.h> +#include <ctype.h> + + + +/*************************************************************************** + VALIDATION +***************************************************************************/ + +#if !defined(CRLF) || (CRLF < 1) || (CRLF > 3) +#error CRLF undefined: must be 1 (CR), 2 (LF) or 3 (CR/LF) +#endif + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define FILE_BUFFER_SIZE 512 + +#define OPEN_FLAG_HAS_CRC 0x10000 + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +enum _text_file_type +{ + TFT_OSD = 0, /* OSD depdendent encoding format used when BOMs missing */ + TFT_UTF8, /* UTF-8 */ + TFT_UTF16BE, /* UTF-16 (big endian) */ + TFT_UTF16LE, /* UTF-16 (little endian) */ + TFT_UTF32BE, /* UTF-32 (UCS-4) (big endian) */ + TFT_UTF32LE /* UTF-32 (UCS-4) (little endian) */ +}; +typedef enum _text_file_type text_file_type; + + +/* typedef struct _core_file core_file -- declared in corefile.h */ +struct _core_file +{ + osd_file * file; /* OSD file handle */ + UINT32 openflags; /* flags we were opened with */ + UINT8 data_allocated; /* was the data allocated by us? */ + UINT8 * data; /* file data, if RAM-based */ + UINT64 offset; /* current file offset */ + UINT64 length; /* total file length */ + text_file_type text_type; /* text output format */ + char back_chars[UTF8_CHAR_MAX]; /* buffer to hold characters for ungetc */ + int back_char_head; /* head of ungetc buffer */ + int back_char_tail; /* tail of ungetc buffer */ + UINT64 bufferbase; /* base offset of internal buffer */ + UINT32 bufferbytes; /* bytes currently loaded into buffer */ + UINT8 buffer[FILE_BUFFER_SIZE]; /* buffer data */ +}; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +/* misc helpers */ +static UINT32 safe_buffer_copy(const void *source, UINT32 sourceoffs, UINT32 sourcelen, void *dest, UINT32 destoffs, UINT32 destlen); + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + is_directory_separator - is a given character + a directory separator? The following logic + works for most platforms +-------------------------------------------------*/ + +INLINE int is_directory_separator(char c) +{ + return (c == '\\' || c == '/' || c == ':'); +} + + + +/*************************************************************************** + FILE OPEN/CLOSE +***************************************************************************/ + +/*------------------------------------------------- + core_fopen - open a file for access and + return an error code +-------------------------------------------------*/ + +file_error core_fopen(const char *filename, UINT32 openflags, core_file **file) +{ + file_error filerr = FILERR_NOT_FOUND; + + /* allocate the file itself */ + *file = malloc(sizeof(**file)); + if (*file == NULL) + return FILERR_OUT_OF_MEMORY; + memset(*file, 0, sizeof(**file)); + + /* attempt to open the file */ + filerr = osd_open(filename, openflags, &(*file)->file, &(*file)->length); + (*file)->openflags = openflags; + + /* handle errors and return */ + if (filerr != FILERR_NONE) + { + core_fclose(*file); + *file = NULL; + } + return filerr; +} + + +/*------------------------------------------------- + core_fopen_ram - open a RAM-based buffer for + file-like access and return an error code +-------------------------------------------------*/ + +file_error core_fopen_ram(const void *data, size_t length, UINT32 openflags, core_file **file) +{ + /* can only do this for read access */ + if ((openflags & OPEN_FLAG_WRITE) != 0) + return FILERR_INVALID_ACCESS; + if ((openflags & OPEN_FLAG_CREATE) != 0) + return FILERR_INVALID_ACCESS; + + /* allocate the file itself */ + *file = malloc(sizeof(**file)); + if (*file == NULL) + return FILERR_OUT_OF_MEMORY; + memset(*file, 0, sizeof(**file)); + + /* claim the buffer */ + (*file)->data = (UINT8 *)data; + (*file)->length = length; + (*file)->openflags = openflags; + + return FILERR_NONE; +} + + +/*------------------------------------------------- + core_fclose - closes a file +-------------------------------------------------*/ + +void core_fclose(core_file *file) +{ + /* close files and free memory */ + if (file->file != NULL) + osd_close(file->file); + if (file->data != NULL && file->data_allocated) + free(file->data); + free(file); +} + + + +/*************************************************************************** + FILE POSITIONING +***************************************************************************/ + +/*------------------------------------------------- + core_fseek - seek within a file +-------------------------------------------------*/ + +int core_fseek(core_file *file, INT64 offset, int whence) +{ + int err = 0; + + /* flush any buffered char */ + file->back_char_head = 0; + file->back_char_tail = 0; + + /* switch off the relative location */ + switch (whence) + { + case SEEK_SET: + file->offset = offset; + break; + + case SEEK_CUR: + file->offset += offset; + break; + + case SEEK_END: + file->offset = file->length + offset; + break; + } + return err; +} + + +/*------------------------------------------------- + core_ftell - return the current file position +-------------------------------------------------*/ + +UINT64 core_ftell(core_file *file) +{ + /* return the current offset */ + return file->offset; +} + + +/*------------------------------------------------- + core_feof - return 1 if we're at the end + of file +-------------------------------------------------*/ + +int core_feof(core_file *file) +{ + /* check for buffered chars */ + if (file->back_char_head != file->back_char_tail) + return 0; + + /* if the offset == length, we're at EOF */ + return (file->offset >= file->length); +} + + +/*------------------------------------------------- + core_fsize - returns the size of a file +-------------------------------------------------*/ + +UINT64 core_fsize(core_file *file) +{ + return file->length; +} + + + +/*************************************************************************** + FILE READ +***************************************************************************/ + +/*------------------------------------------------- + core_fread - read from a file +-------------------------------------------------*/ + +UINT32 core_fread(core_file *file, void *buffer, UINT32 length) +{ + file_error filerr; + UINT32 bytes_read = 0; + + /* flush any buffered char */ + file->back_char_head = 0; + file->back_char_tail = 0; + + /* handle real files */ + if (file->data == NULL) + { + /* if we're within the buffer, consume that first */ + if (file->offset >= file->bufferbase && file->offset < file->bufferbase + file->bufferbytes) + bytes_read += safe_buffer_copy(file->buffer, file->offset - file->bufferbase, file->bufferbytes, buffer, bytes_read, length); + + /* if we've got a small amount left, read it into the buffer first */ + if (bytes_read < length) + { + if (length - bytes_read < sizeof(file->buffer) / 2) + { + /* read as much as makes sense into the buffer */ + file->bufferbase = file->offset + bytes_read; + file->bufferbytes = 0; + filerr = osd_read(file->file, file->buffer, file->bufferbase, sizeof(file->buffer), &file->bufferbytes); + + /* do a bounded copy from the buffer to the destination */ + bytes_read += safe_buffer_copy(file->buffer, 0, file->bufferbytes, buffer, bytes_read, length); + } + + /* read the remainder directly from the file */ + else + { + UINT32 new_bytes_read; + filerr = osd_read(file->file, (UINT8 *)buffer + bytes_read, file->offset + bytes_read, length - bytes_read, &new_bytes_read); + bytes_read += new_bytes_read; + } + } + } + + /* handle RAM-based files */ + else + bytes_read += safe_buffer_copy(file->data, (UINT32)file->offset, file->length, buffer, bytes_read, length); + + /* return the number of bytes read */ + file->offset += bytes_read; + return bytes_read; +} + + +/*------------------------------------------------- + core_fgetc - read a character from a file +-------------------------------------------------*/ + +int core_fgetc(core_file *file) +{ + int result; + + /* refresh buffer, if necessary */ + if (file->back_char_head == file->back_char_tail) + { + utf16_char utf16_buffer[UTF16_CHAR_MAX]; + char utf8_buffer[UTF8_CHAR_MAX]; + char default_buffer[16]; + unicode_char uchar = (unicode_char) ~0; + int readlen, charlen; + + /* do we need to check the byte order marks? */ + if (file->offset == 0) + { + UINT8 bom[4]; + int pos = 0; + + if (core_fread(file, bom, 4) == 4) + { + if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf) + { + file->text_type = TFT_UTF8; + pos = 3; + } + else if (bom[0] == 0x00 && bom[1] == 0x00 && bom[2] == 0xfe && bom[3] == 0xff) + { + file->text_type = TFT_UTF32BE; + pos = 4; + } + else if (bom[0] == 0xff && bom[1] == 0xfe && bom[2] == 0x00 && bom[3] == 0x00) + { + file->text_type = TFT_UTF32LE; + pos = 4; + } + else if (bom[0] == 0xfe && bom[1] == 0xff) + { + file->text_type = TFT_UTF16BE; + pos = 2; + } + else if (bom[0] == 0xff && bom[1] == 0xfe) + { + file->text_type = TFT_UTF16LE; + pos = 2; + } + else + { + file->text_type = TFT_OSD; + pos = 0; + } + } + core_fseek(file, pos, SEEK_SET); + } + + /* fetch the next character */ + switch (file->text_type) + { + default: + case TFT_OSD: + readlen = core_fread(file, default_buffer, sizeof(default_buffer)); + if (readlen > 0) + { + charlen = osd_uchar_from_osdchar(&uchar, default_buffer, readlen / sizeof(default_buffer[0])); + core_fseek(file, (INT64) (charlen * sizeof(default_buffer[0])) - readlen, SEEK_CUR); + } + break; + + case TFT_UTF8: + readlen = core_fread(file, utf8_buffer, sizeof(utf8_buffer)); + if (readlen > 0) + { + charlen = uchar_from_utf8(&uchar, utf8_buffer, readlen / sizeof(utf8_buffer[0])); + core_fseek(file, (INT64) (charlen * sizeof(utf8_buffer[0])) - readlen, SEEK_CUR); + } + break; + + case TFT_UTF16BE: + readlen = core_fread(file, utf16_buffer, sizeof(utf16_buffer)); + if (readlen > 0) + { + charlen = uchar_from_utf16be(&uchar, utf16_buffer, readlen / sizeof(utf16_buffer[0])); + core_fseek(file, (INT64) (charlen * sizeof(utf16_buffer[0])) - readlen, SEEK_CUR); + } + break; + + case TFT_UTF16LE: + readlen = core_fread(file, utf16_buffer, sizeof(utf16_buffer)); + if (readlen > 0) + { + charlen = uchar_from_utf16le(&uchar, utf16_buffer, readlen / sizeof(utf16_buffer[0])); + core_fseek(file, (INT64) (charlen * sizeof(utf16_buffer[0])) - readlen, SEEK_CUR); + } + break; + + case TFT_UTF32BE: + if (core_fread(file, &uchar, sizeof(uchar)) == sizeof(uchar)) + uchar = BIG_ENDIANIZE_INT32(uchar); + break; + + case TFT_UTF32LE: + if (core_fread(file, &uchar, sizeof(uchar)) == sizeof(uchar)) + uchar = LITTLE_ENDIANIZE_INT32(uchar); + break; + } + + if (uchar != ~0) + { + /* place the new character in the ring buffer */ + file->back_char_head = 0; + file->back_char_tail = utf8_from_uchar(file->back_chars, ARRAY_LENGTH(file->back_chars), uchar); +/* assert(file->back_char_tail != -1);*/ + } + } + + /* now read from the ring buffer */ + if (file->back_char_head != file->back_char_tail) + { + result = file->back_chars[file->back_char_head++]; + file->back_char_head %= ARRAY_LENGTH(file->back_chars); + } + else + result = EOF; + + return result; +} + + +/*------------------------------------------------- + core_ungetc - put back a character read from + a file +-------------------------------------------------*/ + +int core_ungetc(int c, core_file *file) +{ + file->back_chars[file->back_char_tail++] = (char) c; + file->back_char_tail %= ARRAY_LENGTH(file->back_chars); + return c; +} + + +/*------------------------------------------------- + core_fgets - read a line from a text file +-------------------------------------------------*/ + +char *core_fgets(char *s, int n, core_file *file) +{ + char *cur = s; + + /* loop while we have characters */ + while (n > 0) + { + int c = core_fgetc(file); + if (c == EOF) + break; + + /* if there's a CR, look for an LF afterwards */ + if (c == 0x0d) + { + int c2 = core_fgetc(file); + if (c2 != 0x0a) + core_ungetc(c2, file); + *cur++ = 0x0d; + n--; + break; + } + + /* if there's an LF, reinterp as a CR for consistency */ + else if (c == 0x0a) + { + *cur++ = 0x0d; + n--; + break; + } + + /* otherwise, pop the character in and continue */ + *cur++ = c; + n--; + } + + /* if we put nothing in, return NULL */ + if (cur == s) + return NULL; + + /* otherwise, terminate */ + if (n > 0) + *cur++ = 0; + return s; +} + + +/*------------------------------------------------- + core_fbuffer - return a pointer to the file + buffer; if it doesn't yet exist, load the + file into RAM first +-------------------------------------------------*/ + +const void *core_fbuffer(core_file *file) +{ + file_error filerr; + UINT32 read_length; + + /* if we already have data, just return it */ + if (file->data != NULL) + return file->data; + + /* allocate some memory */ + file->data = malloc(file->length); + if (file->data == NULL) + return NULL; + file->data_allocated = TRUE; + + /* read the file */ + filerr = osd_read(file->file, file->data, 0, file->length, &read_length); + if (filerr != FILERR_NONE || read_length != file->length) + { + free(file->data); + file->data = NULL; + return NULL; + } + + /* close the file because we don't need it anymore */ + osd_close(file->file); + file->file = NULL; + return file->data; +} + + + +/*************************************************************************** + FILE WRITE +***************************************************************************/ + +/*------------------------------------------------- + core_fwrite - write to a file +-------------------------------------------------*/ + +UINT32 core_fwrite(core_file *file, const void *buffer, UINT32 length) +{ + UINT32 bytes_written = 0; + file_error filerr; + + /* can't write to RAM-based stuff */ + if (file->data != NULL) + return 0; + + /* flush any buffered char */ + file->back_char_head = 0; + file->back_char_tail = 0; + + /* invalidate any buffered data */ + file->bufferbytes = 0; + + /* do the write */ + filerr = osd_write(file->file, buffer, file->offset, length, &bytes_written); + + /* return the number of bytes read */ + file->offset += bytes_written; + file->length = MAX(file->length, file->offset); + return bytes_written; +} + + +/*------------------------------------------------- + core_fputs - write a line to a text file +-------------------------------------------------*/ + +int core_fputs(core_file *f, const char *s) +{ + char convbuf[1024]; + char *pconvbuf = convbuf; + int count = 0; + + /* is this the beginning of the file? if so, write a byte order mark */ + if (f->offset == 0 && !(f->openflags & OPEN_FLAG_NO_BOM)) + { + *pconvbuf++ = 0xef; + *pconvbuf++ = 0xbb; + *pconvbuf++ = 0xbf; + } + + /* convert '\n' to platform dependant line endings */ + while (*s != 0) + { + if (*s == '\n') + { + if (CRLF == 1) /* CR only */ + *pconvbuf++ = 13; + else if (CRLF == 2) /* LF only */ + *pconvbuf++ = 10; + else if (CRLF == 3) /* CR+LF */ + { + *pconvbuf++ = 13; + *pconvbuf++ = 10; + } + } + else + *pconvbuf++ = *s; + s++; + + /* if we overflow, break into chunks */ + if (pconvbuf >= convbuf + ARRAY_LENGTH(convbuf) - 10) + { + count += core_fwrite(f, convbuf, pconvbuf - convbuf); + pconvbuf = convbuf; + } + } + + /* final flush */ + if (pconvbuf != convbuf) + count += core_fwrite(f, convbuf, pconvbuf - convbuf); + + return count; +} + + +/*------------------------------------------------- + core_vfprintf - vfprintf to a text file +-------------------------------------------------*/ + +int core_vfprintf(core_file *f, const char *fmt, va_list va) +{ + char buf[1024]; + vsnprintf(buf, sizeof(buf), fmt, va); + return core_fputs(f, buf); +} + + +/*------------------------------------------------- + core_fprintf - vfprintf to a text file +-------------------------------------------------*/ + +int CLIB_DECL core_fprintf(core_file *f, const char *fmt, ...) +{ + int rc; + va_list va; + va_start(va, fmt); + rc = core_vfprintf(f, fmt, va); + va_end(va); + return rc; +} + + + +/*************************************************************************** + FILENAME UTILITIES +***************************************************************************/ + +/*------------------------------------------------- + core_filename_extract_base - extract the base + name from a filename; note that this makes + assumptions about path separators +-------------------------------------------------*/ + +astring *core_filename_extract_base(astring *result, const char *name, int strip_extension) +{ + /* find the start of the name */ + const char *start = name + strlen(name); + while (start > name && !is_directory_separator(start[-1])) + start--; + + /* copy the rest into an astring */ + astring_cpyc(result, start); + + /* chop the extension if present */ + if (strip_extension) + astring_substr(result, 0, astring_rchr(result, 0, '.')); + return result; +} + + +/*------------------------------------------------- + core_filename_ends_with - does the given + filename end with the specified extension? +-------------------------------------------------*/ + +int core_filename_ends_with(const char *filename, const char *extension) +{ + int namelen = strlen(filename); + int extlen = strlen(extension); + int matches = TRUE; + + /* work backwards checking for a match */ + while (extlen > 0) + if (tolower(filename[--namelen]) != tolower(extension[--extlen])) + { + matches = FALSE; + break; + } + + return matches; +} + + + +/*************************************************************************** + MISC HELPERS +***************************************************************************/ + +/*------------------------------------------------- + safe_buffer_copy - copy safely from one + bounded buffer to another +-------------------------------------------------*/ + +static UINT32 safe_buffer_copy(const void *source, UINT32 sourceoffs, UINT32 sourcelen, void *dest, UINT32 destoffs, UINT32 destlen) +{ + UINT32 sourceavail = sourcelen - sourceoffs; + UINT32 destavail = destlen - destoffs; + UINT32 bytes_to_copy = MIN(sourceavail, destavail); + if (bytes_to_copy > 0) + memcpy((UINT8 *)dest + destoffs, (const UINT8 *)source + sourceoffs, bytes_to_copy); + return bytes_to_copy; +} diff --git a/src/lib/util/corefile.h b/src/lib/util/corefile.h new file mode 100644 index 00000000000..dc9e69ce9f5 --- /dev/null +++ b/src/lib/util/corefile.h @@ -0,0 +1,116 @@ +/*************************************************************************** + + corefile.h + + Core file I/O interface functions and definitions. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#pragma once + +#ifndef __COREFILE_H__ +#define __COREFILE_H__ + +#include <stdarg.h> +#include "osdcore.h" +#include "astring.h" + + + +/*************************************************************************** + ADDITIONAL OPEN FLAGS +***************************************************************************/ + +#define OPEN_FLAG_NO_BOM 0x0100 /* don't output BOM */ + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +typedef struct _core_file core_file; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + + +/* ----- file open/close ----- */ + +/* open a file with the specified filename */ +file_error core_fopen(const char *filename, UINT32 openflags, core_file **file); + +/* open a RAM-based "file" using the given data and length (read-only) */ +file_error core_fopen_ram(const void *data, size_t length, UINT32 openflags, core_file **file); + +/* close an open file */ +void core_fclose(core_file *file); + + + +/* ----- file positioning ----- */ + +/* adjust the file pointer within the file */ +int core_fseek(core_file *file, INT64 offset, int whence); + +/* return the current file pointer */ +UINT64 core_ftell(core_file *file); + +/* return true if we are at the EOF */ +int core_feof(core_file *file); + +/* return the total size of the file */ +UINT64 core_fsize(core_file *file); + + + +/* ----- file read ----- */ + +/* standard binary read from a file */ +UINT32 core_fread(core_file *file, void *buffer, UINT32 length); + +/* read one character from the file */ +int core_fgetc(core_file *file); + +/* put back one character from the file */ +int core_ungetc(int c, core_file *file); + +/* read a full line of text from the file */ +char *core_fgets(char *s, int n, core_file *file); + +/* get a pointer to a buffer that holds the full file data in RAM */ +/* this function may cause the full file data to be read */ +const void *core_fbuffer(core_file *file); + + + +/* ----- file write ----- */ + +/* standard binary write to a file */ +UINT32 core_fwrite(core_file *file, const void *buffer, UINT32 length); + +/* write a line of text to the file */ +int core_fputs(core_file *f, const char *s); + +/* printf-style text write to a file */ +int core_vfprintf(core_file *f, const char *fmt, va_list va); +int CLIB_DECL core_fprintf(core_file *f, const char *fmt, ...); + + + +/* ----- filename utilities ----- */ + +/* extract the base part of a filename (remove extensions and paths) */ +astring *core_filename_extract_base(astring *result, const char *name, int strip_extension); + +/* true if the given filename ends with a particular extension */ +int core_filename_ends_with(const char *filename, const char *extension); + + +#endif /* __COREFILE_H__ */ diff --git a/src/lib/util/corestr.c b/src/lib/util/corestr.c new file mode 100644 index 00000000000..dd7bb1610e8 --- /dev/null +++ b/src/lib/util/corestr.c @@ -0,0 +1,120 @@ +/*************************************************************************** + + corestr.c + + Core string functions used throughout MAME. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +****************************************************************************/ + +#include "corestr.h" +#include <ctype.h> +#include <stdlib.h> + + +/*------------------------------------------------- + core_stricmp - case-insensitive string compare +-------------------------------------------------*/ + +int core_stricmp(const char *s1, const char *s2) +{ + for (;;) + { + int c1 = tolower(*s1++); + int c2 = tolower(*s2++); + if (c1 == 0 || c1 != c2) + return c1 - c2; + } +} + + +/*------------------------------------------------- + core_strnicmp - case-insensitive string compare +-------------------------------------------------*/ + +int core_strnicmp(const char *s1, const char *s2, size_t n) +{ + size_t i; + for (i = 0; i < n; i++) + { + int c1 = tolower(*s1++); + int c2 = tolower(*s2++); + if (c1 == 0 || c1 != c2) + return c1 - c2; + } + + return 0; +} + + +/*------------------------------------------------- + core_strwildcmp - case-insensitive wildcard + string compare (up to 8 characters at the + moment) +-------------------------------------------------*/ + +int core_strwildcmp(const char *sp1, const char *sp2) +{ + char s1[9], s2[9]; + int i, l1, l2; + char *p; + + strncpy(s1, sp1, 8); s1[8] = 0; if (s1[0] == 0) strcpy(s1, "*"); + + strncpy(s2, sp2, 8); s2[8] = 0; if (s2[0] == 0) strcpy(s2, "*"); + + p = strchr(s1, '*'); + if (p) + { + for (i = p - s1; i < 8; i++) s1[i] = '?'; + s1[8] = 0; + } + + p = strchr(s2, '*'); + if (p) + { + for (i = p - s2; i < 8; i++) s2[i] = '?'; + s2[8] = 0; + } + + l1 = (int)strlen(s1); + if (l1 < 8) + { + for (i = l1 + 1; i < 8; i++) s1[i] = ' '; + s1[8] = 0; + } + + l2 = (int)strlen(s2); + if (l2 < 8) + { + for (i = l2 + 1; i < 8; i++) s2[i] = ' '; + s2[8] = 0; + } + + for (i = 0; i < 8; i++) + { + if (s1[i] == '?' && s2[i] != '?') s1[i] = s2[i]; + if (s2[i] == '?' && s1[i] != '?') s2[i] = s1[i]; + } + + return core_stricmp(s1, s2); +} + + +/*------------------------------------------------- + core_strdup - string duplication via malloc +-------------------------------------------------*/ + +char *core_strdup(const char *str) +{ + char *cpy = NULL; + if (str != NULL) + { + cpy = malloc(strlen(str) + 1); + if (cpy != NULL) + strcpy(cpy, str); + } + return cpy; +} diff --git a/src/lib/util/corestr.h b/src/lib/util/corestr.h new file mode 100644 index 00000000000..cd2ef4028b7 --- /dev/null +++ b/src/lib/util/corestr.h @@ -0,0 +1,60 @@ +/*************************************************************************** + + corestr.h + + Core string functions used throughout MAME. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#pragma once + +#ifndef __CORESTR_H__ +#define __CORESTR_H__ + +#include <string.h> + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +/* since stricmp is not part of the standard, we use this instead */ +int core_stricmp(const char *s1, const char *s2); + +/* this macro prevents people from using stricmp directly */ +#undef stricmp +#define stricmp !MUST_USE_CORE_STRICMP_INSTEAD! + +/* this macro prevents people from using strcasecmp directly */ +#undef strcasecmp +#define strcasecmp !MUST_USE_CORE_STRICMP_INSTEAD! + + +/* since strnicmp is not part of the standard, we use this instead */ +int core_strnicmp(const char *s1, const char *s2, size_t n); + +/* this macro prevents people from using strnicmp directly */ +#undef strnicmp +#define strnicmp !MUST_USE_CORE_STRNICMP_INSTEAD! + +/* this macro prevents people from using strncasecmp directly */ +#undef strncasecmp +#define strncasecmp !MUST_USE_CORE_STRNICMP_INSTEAD! + + +/* since strdup is not part of the standard, we use this instead */ +char *core_strdup(const char *str); + +/* this macro prevents people from using strdup directly */ +#undef strdup +#define strdup !MUST_USE_CORE_STRDUP_INSTEAD! + + +/* additional string compare helper */ +int core_strwildcmp(const char *sp1, const char *sp2); + + +#endif /* __CORESTR_H__ */ diff --git a/src/lib/util/coreutil.c b/src/lib/util/coreutil.c new file mode 100644 index 00000000000..daaf116dc9c --- /dev/null +++ b/src/lib/util/coreutil.c @@ -0,0 +1,102 @@ +/*************************************************************************** + + coreutil.c + + Miscellaneous utility code + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +****************************************************************************/ + +#include "coreutil.h" + + +/*************************************************************************** + BINARY CODED DECIMAL HELPERS +***************************************************************************/ + +int bcd_adjust(int value) +{ + if ((value & 0xf) >= 0xa) + value = value + 0x10 - 0xa; + if ((value & 0xf0) >= 0xa0) + value = value - 0xa0 + 0x100; + return value; +} + + +UINT32 dec_2_bcd(UINT32 a) +{ + UINT32 result = 0; + int shift = 0; + + while (a != 0) + { + result |= (a % 10) << shift; + a /= 10; + shift += 4; + } + return result; +} + + +UINT32 bcd_2_dec(UINT32 a) +{ + UINT32 result = 0; + UINT32 scale = 1; + + while (a != 0) + { + result += (a & 0x0f) * scale; + a >>= 4; + scale *= 10; + } + return result; +} + + + +/*************************************************************************** + GREGORIAN CALENDAR HELPERS +***************************************************************************/ + +int gregorian_is_leap_year(int year) +{ + return !(year % 100 ? year % 4 : year % 400); +} + + +/* months are one counted */ +int gregorian_days_in_month(int month, int year) +{ + if (month == 2) + return gregorian_is_leap_year(year) ? 29 : 28; + else if (month == 4 || month == 6 || month == 9 || month == 11) + return 30; + else + return 31; +} + + +/*************************************************************************** + MISC +***************************************************************************/ + +void rand_memory(void *memory, size_t length) +{ + UINT8 *bytes = (UINT8 *) memory; + UINT32 currand; + size_t i; + + currand = rand(); + currand <<= 15; + currand ^= rand(); + + for (i = 0; i < length; i++) + { + currand = currand * 214013 + 2531011; + bytes[i] = (UINT8) (currand >> 16); + } +} + diff --git a/src/lib/util/coreutil.h b/src/lib/util/coreutil.h new file mode 100644 index 00000000000..e251d61ca80 --- /dev/null +++ b/src/lib/util/coreutil.h @@ -0,0 +1,44 @@ +/*************************************************************************** + + coreutil.h + + Miscellaneous utility code + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#pragma once + +#ifndef __COREUTIL_H__ +#define __COREUTIL_H__ + +#include "osdcomm.h" + + +/*************************************************************************** + BINARY CODED DECIMAL HELPERS +***************************************************************************/ + +int bcd_adjust(int value); +UINT32 dec_2_bcd(UINT32 a); +UINT32 bcd_2_dec(UINT32 a); + + +/*************************************************************************** + GREGORIAN CALENDAR HELPERS +***************************************************************************/ + +int gregorian_is_leap_year(int year); +int gregorian_days_in_month(int month, int year); + + +/*************************************************************************** + MISC +***************************************************************************/ + +void rand_memory(void *memory, size_t length); + + +#endif /* __COREUTIL_H__ */ diff --git a/src/lib/util/harddisk.c b/src/lib/util/harddisk.c new file mode 100644 index 00000000000..a4edf8524d9 --- /dev/null +++ b/src/lib/util/harddisk.c @@ -0,0 +1,170 @@ +/*************************************************************************** + + Generic MAME hard disk implementation, with differencing files + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#include "harddisk.h" + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +struct _hard_disk_file +{ + chd_file * chd; /* CHD file */ + hard_disk_info info; /* hard disk info */ + UINT32 hunksectors; /* sectors per hunk */ + UINT32 cachehunk; /* which hunk is cached */ + UINT8 * cache; /* cache of the current hunk */ +}; + + + +/*************************************************************************** + CORE IMPLEMENTATION +***************************************************************************/ + +/*------------------------------------------------- + hard_disk_open - open a hard disk handle, + given a chd_file +-------------------------------------------------*/ + +hard_disk_file *hard_disk_open(chd_file *chd) +{ + int cylinders, heads, sectors, sectorbytes; + hard_disk_file *file; + char metadata[256]; + chd_error err; + + /* punt if no CHD */ + if (chd == NULL) + return NULL; + + /* read the hard disk metadata */ + err = chd_get_metadata(chd, HARD_DISK_METADATA_TAG, 0, metadata, sizeof(metadata), NULL, NULL); + if (err != CHDERR_NONE) + return NULL; + + /* parse the metadata */ + if (sscanf(metadata, HARD_DISK_METADATA_FORMAT, &cylinders, &heads, §ors, §orbytes) != 4) + return NULL; + + /* allocate memory for the hard disk file */ + file = malloc(sizeof(hard_disk_file)); + if (file == NULL) + return NULL; + + /* fill in the data */ + file->chd = chd; + file->info.cylinders = cylinders; + file->info.heads = heads; + file->info.sectors = sectors; + file->info.sectorbytes = sectorbytes; + file->hunksectors = chd_get_header(chd)->hunkbytes / file->info.sectorbytes; + file->cachehunk = -1; + + /* allocate a cache */ + file->cache = malloc(chd_get_header(chd)->hunkbytes); + if (file->cache == NULL) + { + free(file); + return NULL; + } + + return file; +} + + +/*------------------------------------------------- + hard_disk_close - close a hard disk handle +-------------------------------------------------*/ + +void hard_disk_close(hard_disk_file *file) +{ + /* free the cache */ + if (file->cache != NULL) + free(file->cache); + free(file); +} + + +/*------------------------------------------------- + hard_disk_get_chd - get a handle to a CHD + from a hard disk +-------------------------------------------------*/ + +chd_file *hard_disk_get_chd(hard_disk_file *file) +{ + return file->chd; +} + + +/*------------------------------------------------- + hard_disk_get_info - return information about + a hard disk +-------------------------------------------------*/ + +hard_disk_info *hard_disk_get_info(hard_disk_file *file) +{ + return &file->info; +} + + +/*------------------------------------------------- + hard_disk_read - read sectors from a hard + disk +-------------------------------------------------*/ + +UINT32 hard_disk_read(hard_disk_file *file, UINT32 lbasector, void *buffer) +{ + UINT32 hunknum = lbasector / file->hunksectors; + UINT32 sectoroffs = lbasector % file->hunksectors; + + /* if we haven't cached this hunk, read it now */ + if (file->cachehunk != hunknum) + { + chd_error err = chd_read(file->chd, hunknum, file->cache); + if (err != CHDERR_NONE) + return 0; + file->cachehunk = hunknum; + } + + /* copy out the requested sector */ + memcpy(buffer, &file->cache[sectoroffs * file->info.sectorbytes], file->info.sectorbytes); + return 1; +} + + +/*------------------------------------------------- + hard_disk_write - write sectors to a hard + disk +-------------------------------------------------*/ + +UINT32 hard_disk_write(hard_disk_file *file, UINT32 lbasector, const void *buffer) +{ + UINT32 hunknum = lbasector / file->hunksectors; + UINT32 sectoroffs = lbasector % file->hunksectors; + chd_error err; + + /* if we haven't cached this hunk, read it now */ + if (file->cachehunk != hunknum) + { + err = chd_read(file->chd, hunknum, file->cache); + if (err != CHDERR_NONE) + return 0; + file->cachehunk = hunknum; + } + + /* copy in the requested data */ + memcpy(&file->cache[sectoroffs * file->info.sectorbytes], buffer, file->info.sectorbytes); + + /* write it back out */ + err = chd_write(file->chd, hunknum, file->cache); + return (err == CHDERR_NONE) ? 1 : 0; +} diff --git a/src/lib/util/harddisk.h b/src/lib/util/harddisk.h new file mode 100644 index 00000000000..2636fc80094 --- /dev/null +++ b/src/lib/util/harddisk.h @@ -0,0 +1,51 @@ +/*************************************************************************** + + harddisk.h + + Generic MAME hard disk implementation, with differencing files + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#pragma once + +#ifndef __HARDDISK_H__ +#define __HARDDISK_H__ + +#include "osdcore.h" +#include "chd.h" + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +typedef struct _hard_disk_file hard_disk_file; + +typedef struct _hard_disk_info hard_disk_info; +struct _hard_disk_info +{ + UINT32 cylinders; + UINT32 heads; + UINT32 sectors; + UINT32 sectorbytes; +}; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +hard_disk_file *hard_disk_open(chd_file *chd); +void hard_disk_close(hard_disk_file *file); + +chd_file *hard_disk_get_chd(hard_disk_file *file); +hard_disk_info *hard_disk_get_info(hard_disk_file *file); + +UINT32 hard_disk_read(hard_disk_file *file, UINT32 lbasector, void *buffer); +UINT32 hard_disk_write(hard_disk_file *file, UINT32 lbasector, const void *buffer); + +#endif /* __HARDDISK_H__ */ diff --git a/src/lib/util/huffman.c b/src/lib/util/huffman.c new file mode 100644 index 00000000000..0465f5d94d3 --- /dev/null +++ b/src/lib/util/huffman.c @@ -0,0 +1,934 @@ +/*************************************************************************** + + huffman.c + + Video compression and decompression helpers. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**************************************************************************** + + Maximum codelength is officially (alphabetsize - 1). This would be 255 bits + (since we use 1 byte values). However, it is also dependent upon the number + of samples used, as follows: + + 2 bits -> 3..4 samples + 3 bits -> 5..7 samples + 4 bits -> 8..12 samples + 5 bits -> 13..20 samples + 6 bits -> 21..33 samples + 7 bits -> 34..54 samples + 8 bits -> 55..88 samples + 9 bits -> 89..143 samples + 10 bits -> 144..232 samples + 11 bits -> 233..376 samples + 12 bits -> 377..609 samples + 13 bits -> 610..986 samples + 14 bits -> 987..1596 samples + 15 bits -> 1597..2583 samples + 16 bits -> 2584..4180 samples -> note that a 4k data size guarantees codelength <= 16 bits + 17 bits -> 4181..6764 samples + 18 bits -> 6765..10945 samples + 19 bits -> 10946..17710 samples + 20 bits -> 17711..28656 samples + 21 bits -> 28657..46367 samples + 22 bits -> 46368..75024 samples + 23 bits -> 75025..121392 samples + 24 bits -> 121393..196417 samples + 25 bits -> 196418..317810 samples + 26 bits -> 317811..514228 samples + 27 bits -> 514229..832039 samples + 28 bits -> 832040..1346268 samples + 29 bits -> 1346269..2178308 samples + 30 bits -> 2178309..3524577 samples + 31 bits -> 3524578..5702886 samples + 32 bits -> 5702887..9227464 samples + + Looking at it differently, here is where powers of 2 fall into these buckets: + + 256 samples -> 11 bits max + 512 samples -> 12 bits max + 1k samples -> 14 bits max + 2k samples -> 15 bits max + 4k samples -> 16 bits max + 8k samples -> 18 bits max + 16k samples -> 19 bits max + 32k samples -> 21 bits max + 64k samples -> 22 bits max + 128k samples -> 24 bits max + 256k samples -> 25 bits max + 512k samples -> 27 bits max + 1M samples -> 28 bits max + 2M samples -> 29 bits max + 4M samples -> 31 bits max + 8M samples -> 32 bits max + +***************************************************************************/ + +#include "huffman.h" + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define MAX_HUFFMAN_NODES (256 + 256) + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +typedef struct _bit_buffer bit_buffer; +struct _bit_buffer +{ + UINT32 buffer; + int bits; + union + { + const UINT8 *read; + UINT8 * write; + } data; + UINT32 doffset; + UINT32 dlength; + int overflow; +}; + + +typedef struct _huffman_node huffman_node; +struct _huffman_node +{ + huffman_node * parent; + UINT32 count; + UINT32 weight; + UINT32 bits; + UINT8 numbits; +}; + + +struct _huffman_context +{ + UINT8 maxbits; + UINT8 lookupdirty; + huffman_node huffnode[MAX_HUFFMAN_NODES]; + UINT32 lookupmask; + huffman_lookup_value *lookup; +}; + + + +/*************************************************************************** + PROTOTYPES +***************************************************************************/ + +static void huffman_write_rle_tree_bits(bit_buffer *bitbuf, int value, int repcount, int numbits); +static int CLIB_DECL huffman_tree_node_compare(const void *item1, const void *item2); +static int huffman_build_tree(huffman_context *context, const UINT32 *datahisto, UINT32 totaldata, UINT32 totalweight); +static huffman_error huffman_assign_canonical_codes(huffman_context *context); +static huffman_error huffman_build_lookup_table(huffman_context *context); + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + bit_buffer_write_init - initialize a bit + buffer for writing +-------------------------------------------------*/ + +INLINE void bit_buffer_write_init(bit_buffer *bitbuf, UINT8 *data, UINT32 dlength) +{ + /* fill in the basic data structure */ + bitbuf->buffer = 0; + bitbuf->bits = 0; + bitbuf->data.write = data; + bitbuf->doffset = 0; + bitbuf->dlength = dlength; + bitbuf->overflow = FALSE; +} + + +/*------------------------------------------------- + bit_buffer_write - write 'numbits' to the + bit buffer, assuming that 'newbits' is right- + justified +-------------------------------------------------*/ + +INLINE void bit_buffer_write(bit_buffer *bitbuf, UINT32 newbits, int numbits) +{ + /* flush the buffer if we're going to overflow it */ + if (bitbuf->bits + numbits > 32) + while (bitbuf->bits >= 8) + { + if (bitbuf->doffset < bitbuf->dlength) + bitbuf->data.write[bitbuf->doffset] = bitbuf->buffer >> 24; + else + bitbuf->overflow = TRUE; + bitbuf->doffset++; + bitbuf->buffer <<= 8; + bitbuf->bits -= 8; + } + + /* shift the bits to the top */ + newbits <<= 32 - numbits; + + /* now shift it down to account for the number of bits we already have and OR them in */ + bitbuf->buffer |= newbits >> bitbuf->bits; + bitbuf->bits += numbits; +} + + +/*------------------------------------------------- + bit_buffer_flush - flush any bits in the write + buffer and return the final data offset +-------------------------------------------------*/ + +INLINE UINT32 bit_buffer_flush(bit_buffer *bitbuf) +{ + while (bitbuf->bits > 0) + { + if (bitbuf->doffset < bitbuf->dlength) + bitbuf->data.write[bitbuf->doffset] = bitbuf->buffer >> 24; + else + bitbuf->overflow = TRUE; + bitbuf->doffset++; + bitbuf->buffer <<= 8; + bitbuf->bits -= 8; + } + return bitbuf->doffset; +} + + +/*------------------------------------------------- + bit_buffer_read_init - initialize a bit + buffer for reading +-------------------------------------------------*/ + +INLINE void bit_buffer_read_init(bit_buffer *bitbuf, const UINT8 *data, UINT32 dlength) +{ + /* fill in the basic data structure */ + bitbuf->buffer = 0; + bitbuf->bits = 0; + bitbuf->data.read = data; + bitbuf->doffset = 0; + bitbuf->dlength = dlength; + bitbuf->overflow = FALSE; +} + + +/*------------------------------------------------- + bit_buffer_read - read 'numbits' bits from + the buffer, returning the right-justified +-------------------------------------------------*/ + +INLINE UINT32 bit_buffer_read(bit_buffer *bitbuf, int numbits) +{ + UINT32 result; + + /* fetch data if we need more */ + if (numbits > bitbuf->bits) + { + while (bitbuf->bits <= 24) + { + if (bitbuf->doffset < bitbuf->dlength) + bitbuf->buffer |= bitbuf->data.read[bitbuf->doffset] << (24 - bitbuf->bits); + bitbuf->doffset++; + bitbuf->bits += 8; + } + if (numbits > bitbuf->bits) + bitbuf->overflow = TRUE; + } + + /* return the data */ + result = bitbuf->buffer >> (32 - numbits); + bitbuf->buffer <<= numbits; + bitbuf->bits -= numbits; + return result; +} + + +/*------------------------------------------------- + bit_buffer_read_offset - return the current + rounded byte reading offset +-------------------------------------------------*/ + +INLINE UINT32 bit_buffer_read_offset(bit_buffer *bitbuf) +{ + UINT32 result = bitbuf->doffset; + int bits = bitbuf->bits; + while (bits >= 8) + { + result--; + bits -= 8; + } + return result; +} + + + +/*************************************************************************** + IMPLEMENTATION +***************************************************************************/ + +/*------------------------------------------------- + huffman_create_context - create an encoding/ + decoding context +-------------------------------------------------*/ + +huffman_error huffman_create_context(huffman_context **context, int maxbits) +{ + /* limit to 24 bits */ + if (maxbits > 24) + return HUFFERR_TOO_MANY_BITS; + + /* allocate a context */ + *context = malloc(sizeof(**context)); + if (*context == NULL) + return HUFFERR_OUT_OF_MEMORY; + + /* set the info */ + memset(*context, 0, sizeof(**context)); + (*context)->maxbits = maxbits; + (*context)->lookupmask = (1 << maxbits) - 1; + (*context)->lookupdirty = TRUE; + + return HUFFERR_NONE; +} + + +/*------------------------------------------------- + huffman_free_context - free an encoding/ + decoding context +-------------------------------------------------*/ + +void huffman_free_context(huffman_context *context) +{ + if (context->lookup != NULL) + free(context->lookup); + free(context); +} + + +/*------------------------------------------------- + huffman_compute_tree - compute an optimal + huffman tree for the given source data +-------------------------------------------------*/ + +huffman_error huffman_compute_tree(huffman_context *context, const UINT8 *source, UINT32 slength, UINT32 sstride) +{ + UINT32 lowerweight, upperweight; + UINT32 datahisto[256]; + int i; + + /* build the data histogram */ + memset(datahisto, 0, sizeof(datahisto)); + for (i = 0; i < slength; i += sstride) + datahisto[source[i]]++; + + /* binary search to achieve the optimum encoding */ + lowerweight = 0; + upperweight = slength * 2; + while (TRUE) + { + UINT32 curweight = (upperweight + lowerweight) / 2; + int curmaxbits; + + /* build a tree using the current weight */ + curmaxbits = huffman_build_tree(context, datahisto, slength, curweight); + + /* apply binary search here */ + if (curmaxbits <= context->maxbits) + { + lowerweight = curweight; + + /* early out if it worked with the raw weights, or if we're done searching */ + if (curweight == slength || (upperweight - lowerweight) <= 1) + break; + } + else + upperweight = curweight; + } + + /* assign canonical codes for all nodes based on their code lengths */ + return huffman_assign_canonical_codes(context); +} + + +/*------------------------------------------------- + huffman_import_tree - import a huffman tree + from a source data stream +-------------------------------------------------*/ + +huffman_error huffman_import_tree(huffman_context *context, const UINT8 *source, UINT32 slength, UINT32 *actlength) +{ + huffman_error error; + bit_buffer bitbuf; + int curnode; + int numbits; + + /* initialize the input buffer */ + bit_buffer_read_init(&bitbuf, source, slength); + + /* bits per entry depends on the maxbits */ + if (context->maxbits >= 16) + numbits = 5; + else if (context->maxbits >= 8) + numbits = 4; + else + numbits = 3; + + /* loop until we read all the nodes */ + for (curnode = 0; curnode < 256; ) + { + int nodebits = bit_buffer_read(&bitbuf, numbits); + + /* a non-one value is just raw */ + if (nodebits != 1) + context->huffnode[curnode++].numbits = nodebits; + + /* a one value is an escape code */ + else + { + nodebits = bit_buffer_read(&bitbuf, numbits); + + /* a double 1 is just a single 1 */ + if (nodebits == 1) + context->huffnode[curnode++].numbits = nodebits; + + /* otherwise, we need one for value for the repeat count */ + else + { + int repcount = bit_buffer_read(&bitbuf, numbits) + 3; + while (repcount--) + context->huffnode[curnode++].numbits = nodebits; + } + } + } + + /* assign canonical codes for all nodes based on their code lengths */ + error = huffman_assign_canonical_codes(context); + if (error != HUFFERR_NONE) + return error; + + /* make sure we ended up with the right number */ + if (curnode != 256) + return HUFFERR_INVALID_DATA; + + *actlength = bit_buffer_read_offset(&bitbuf); + return bitbuf.overflow ? HUFFERR_INPUT_BUFFER_TOO_SMALL : HUFFERR_NONE; +} + + +/*------------------------------------------------- + huffman_export_tree - export a huffman tree + to a target data stream +-------------------------------------------------*/ + +huffman_error huffman_export_tree(huffman_context *context, UINT8 *dest, UINT32 dlength, UINT32 *actlength) +{ + bit_buffer bitbuf; + int repcount; + int lastval; + int numbits; + int i; + + /* initialize the output buffer */ + bit_buffer_write_init(&bitbuf, dest, dlength); + + /* bits per entry depends on the maxbits */ + if (context->maxbits >= 16) + numbits = 5; + else if (context->maxbits >= 8) + numbits = 4; + else + numbits = 3; + + /* RLE encode the lengths */ + lastval = ~0; + repcount = 0; + for (i = 0; i < 256; i++) + { + int newval = context->huffnode[i].numbits; + + /* if we match the previous value, just bump the repcount */ + if (newval == lastval) + repcount++; + + /* otherwise, we need to flush the previous repeats */ + else + { + if (repcount != 0) + huffman_write_rle_tree_bits(&bitbuf, lastval, repcount, numbits); + lastval = newval; + repcount = 1; + } + } + + /* flush the last value */ + huffman_write_rle_tree_bits(&bitbuf, lastval, repcount, numbits); + *actlength = bit_buffer_flush(&bitbuf); + return bitbuf.overflow ? HUFFERR_OUTPUT_BUFFER_TOO_SMALL : HUFFERR_NONE; +} + + +/*------------------------------------------------- + huffman_get_lookup_table - return a pointer to + the lookup table +-------------------------------------------------*/ + +huffman_error huffman_get_lookup_table(huffman_context *context, const huffman_lookup_value **table) +{ + if (context->lookupdirty) + { + huffman_error error = huffman_build_lookup_table(context); + if (error != HUFFERR_NONE) + return error; + } + *table = context->lookup; + return HUFFERR_NONE; +} + + +/*------------------------------------------------- + huffman_encode_data - encode data using the + current tree +-------------------------------------------------*/ + +huffman_error huffman_encode_data(huffman_context *context, const UINT8 *source, UINT32 slength, UINT8 *dest, UINT32 dlength, UINT32 *actlength) +{ + bit_buffer bitbuf; + UINT32 soffset; + + /* initialize the output buffer */ + bit_buffer_write_init(&bitbuf, dest, dlength); + + /* loop over source data and encode */ + for (soffset = 0; soffset < slength; soffset++) + { + huffman_node *node = &context->huffnode[source[soffset]]; + bit_buffer_write(&bitbuf, node->bits, node->numbits); + } + *actlength = bit_buffer_flush(&bitbuf); + return bitbuf.overflow ? HUFFERR_OUTPUT_BUFFER_TOO_SMALL : HUFFERR_NONE; +} + + +/*------------------------------------------------- + huffman_encode_data_interleaved_2 - encode + alternating data with two contexts +-------------------------------------------------*/ + +huffman_error huffman_encode_data_interleaved_2(huffman_context *context1, huffman_context *context2, const UINT8 *source, UINT32 slength, UINT8 *dest, UINT32 dlength, UINT32 *actlength) +{ + bit_buffer bitbuf; + UINT32 soffset; + + /* initialize the output buffer */ + bit_buffer_write_init(&bitbuf, dest, dlength); + + /* loop over source data and encode */ + for (soffset = 0; soffset < slength; soffset += 2) + { + huffman_node *node; + + node = &context1->huffnode[source[soffset + 0]]; + bit_buffer_write(&bitbuf, node->bits, node->numbits); + + node = &context2->huffnode[source[soffset + 1]]; + bit_buffer_write(&bitbuf, node->bits, node->numbits); + } + *actlength = bit_buffer_flush(&bitbuf); + return bitbuf.overflow ? HUFFERR_OUTPUT_BUFFER_TOO_SMALL : HUFFERR_NONE; +} + + +/*------------------------------------------------- + huffman_decode_data - decode data using the + current tree +-------------------------------------------------*/ + +huffman_error huffman_decode_data(huffman_context *context, const UINT8 *source, UINT32 slength, UINT8 *dest, UINT32 dlength, UINT32 *actlength) +{ + int maxbits = context->maxbits; + int shiftbits = 32 - maxbits; + const huffman_lookup_value *table; + int overflow = FALSE; + huffman_error error; + UINT32 doffset = 0; + UINT32 soffset = 0; + UINT32 bitbuf = 0; + int sbits = 0; + + /* regenerate the lookup table if necessary */ + error = huffman_get_lookup_table(context, &table); + if (error != HUFFERR_NONE) + return error; + + /* decode until we process all of the destination data */ + for (doffset = 0; doffset < dlength; doffset++) + { + huffman_lookup_value lookup; + + /* if we don't have enough bits, load up the buffer */ + if (sbits < maxbits) + { + while (sbits <= 24) + { + if (soffset < slength) + bitbuf |= source[soffset] << (24 - sbits); + soffset++; + sbits += 8; + } + if (sbits < maxbits) + overflow = TRUE; + } + + /* lookup the data */ + lookup = table[bitbuf >> shiftbits]; + + /* store the upper byte */ + dest[doffset] = lookup >> 8; + + /* count the bits */ + lookup &= 0x1f; + bitbuf <<= lookup; + sbits -= lookup; + } + + /* back off soffset while we have whole bytes */ + while (sbits >= 8) + { + sbits -= 8; + soffset--; + } + *actlength = soffset; + return overflow ? HUFFERR_INPUT_BUFFER_TOO_SMALL : HUFFERR_NONE; +} + + +/*------------------------------------------------- + huffman_decode_data_interleaved_2 - decode + interleaved data using two contexts +-------------------------------------------------*/ + +huffman_error huffman_decode_data_interleaved_2(huffman_context *context1, huffman_context *context2, const UINT8 *source, UINT32 slength, UINT8 *dest, UINT32 dlength, UINT32 *actlength) +{ + int maxbits1 = context1->maxbits, maxbits2 = context2->maxbits; + const huffman_lookup_value *table1, *table2; + int shiftbits1 = 32 - maxbits1; + int shiftbits2 = 32 - maxbits2; + int overflow = FALSE; + huffman_error error; + UINT32 doffset = 0; + UINT32 soffset = 0; + UINT32 bitbuf = 0; + int sbits = 0; + + /* regenerate the lookup table if necessary */ + error = huffman_get_lookup_table(context1, &table1); + if (error != HUFFERR_NONE) + return error; + error = huffman_get_lookup_table(context2, &table2); + if (error != HUFFERR_NONE) + return error; + + /* decode until we process all of the destination data */ + for (doffset = 0; doffset < dlength; doffset += 2) + { + huffman_lookup_value lookup; + + /* if we don't have enough bits, load up the buffer */ + if (sbits < maxbits1) + { + while (sbits <= 24) + { + if (soffset < slength) + bitbuf |= source[soffset] << (24 - sbits); + soffset++; + sbits += 8; + } + if (sbits < maxbits1) + overflow = TRUE; + } + + /* lookup the data */ + lookup = table1[bitbuf >> shiftbits1]; + + /* store the upper byte */ + dest[doffset + 0] = lookup >> 8; + + /* count the bits */ + lookup &= 0x1f; + bitbuf <<= lookup; + sbits -= lookup; + + /* if we don't have enough bits, load up the buffer */ + if (sbits < maxbits2) + { + while (sbits <= 24) + { + if (soffset < slength) + bitbuf |= source[soffset] << (24 - sbits); + soffset++; + sbits += 8; + } + if (sbits < maxbits2) + overflow = TRUE; + } + + /* lookup the data */ + lookup = table2[bitbuf >> shiftbits2]; + + /* store the upper byte */ + dest[doffset + 1] = lookup >> 8; + + /* count the bits */ + lookup &= 0x1f; + bitbuf <<= lookup; + sbits -= lookup; + } + + /* back off soffset while we have whole bytes */ + while (sbits >= 8) + { + sbits -= 8; + soffset--; + } + *actlength = soffset; + return overflow ? HUFFERR_INPUT_BUFFER_TOO_SMALL : HUFFERR_NONE; +} + + +/*------------------------------------------------- + huffman_write_rle_tree_bits - write an RLE + encoded set of data to a target stream +-------------------------------------------------*/ + +static void huffman_write_rle_tree_bits(bit_buffer *bitbuf, int value, int repcount, int numbits) +{ + /* loop until we have output all of the repeats */ + while (repcount > 0) + { + /* if we have a 1, write it twice as it is an escape code */ + if (value == 1) + { + bit_buffer_write(bitbuf, 1, numbits); + bit_buffer_write(bitbuf, 1, numbits); + repcount--; + } + + /* if we have two or fewer in a row, write them raw */ + else if (repcount <= 2) + { + bit_buffer_write(bitbuf, value, numbits); + repcount--; + } + + /* otherwise, write a triple using 1 as the escape code */ + else + { + int cur_reps = MIN(repcount - 3, (1 << numbits) - 1); + bit_buffer_write(bitbuf, 1, numbits); + bit_buffer_write(bitbuf, value, numbits); + bit_buffer_write(bitbuf, cur_reps, numbits); + repcount -= cur_reps + 3; + } + } +} + + +/*------------------------------------------------- + huffman_tree_node_compare - compare two + tree nodes by weight +-------------------------------------------------*/ + +static int CLIB_DECL huffman_tree_node_compare(const void *item1, const void *item2) +{ + const huffman_node *node1 = *(const huffman_node **)item1; + const huffman_node *node2 = *(const huffman_node **)item2; + return node2->weight - node1->weight; +} + + +/*------------------------------------------------- + huffman_build_tree - build a huffman tree + based on the data distribution +-------------------------------------------------*/ + +static int huffman_build_tree(huffman_context *context, const UINT32 *datahisto, UINT32 totaldata, UINT32 totalweight) +{ + huffman_node *list[256]; + int listitems; + int nextalloc; + int maxbits; + int i; + + /* make a list of all non-zero nodes */ + listitems = 0; + memset(context->huffnode, 0, 256 * sizeof(context->huffnode[0])); + for (i = 0; i < 256; i++) + if (datahisto[i] != 0) + { + list[listitems++] = &context->huffnode[i]; + context->huffnode[i].count = datahisto[i]; + + /* scale the weight by the current effective length, ensuring we don't go to 0 */ + context->huffnode[i].weight = (UINT64)datahisto[i] * (UINT64)totalweight / (UINT64)totaldata; + if (context->huffnode[i].weight == 0) + context->huffnode[i].weight = 1; + } + + /* sort the list by weight, largest weight first */ + qsort(list, listitems, sizeof(list[0]), huffman_tree_node_compare); + + /* now build the tree */ + nextalloc = 256; + while (listitems > 1) + { + huffman_node *node0, *node1, *newnode; + + /* remove lowest two items */ + node1 = list[--listitems]; + node0 = list[--listitems]; + + /* create new node */ + newnode = &context->huffnode[nextalloc++]; + newnode->parent = NULL; + node0->parent = node1->parent = newnode; + newnode->weight = node0->weight + node1->weight; + + /* insert into list at appropriate location */ + for (i = 0; i < listitems; i++) + if (newnode->weight > list[i]->weight) + { + memmove(&list[i+1], &list[i], (listitems - i) * sizeof(list[0])); + break; + } + list[i] = newnode; + listitems++; + } + + /* compute the number of bits in each code, and fill in another histogram */ + maxbits = 0; + for (i = 0; i < 256; i++) + { + huffman_node *node = &context->huffnode[i]; + node->numbits = 0; + + /* if we have a non-zero weight, compute the number of bits */ + if (node->weight > 0) + { + huffman_node *curnode; + + /* determine the number of bits for this node */ + for (curnode = node; curnode->parent != NULL; curnode = curnode->parent) + node->numbits++; + if (node->numbits == 0) + node->numbits = 1; + + /* keep track of the max */ + maxbits = MAX(maxbits, node->numbits); + } + } + + return maxbits; +} + + +/*------------------------------------------------- + huffman_assign_canonical_codes - assign + canonical codes to all the nodes based on the + number of bits in each +-------------------------------------------------*/ + +static huffman_error huffman_assign_canonical_codes(huffman_context *context) +{ + UINT32 bithisto[33]; + int curstart; + int i; + + /* build up a histogram of bit lengths */ + memset(bithisto, 0, sizeof(bithisto)); + for (i = 0; i < 256; i++) + { + huffman_node *node = &context->huffnode[i]; + if (node->numbits > context->maxbits) + return HUFFERR_INTERNAL_INCONSISTENCY; + if (node->numbits <= 32) + bithisto[node->numbits]++; + } + + /* for each code length, determine the starting code number */ + curstart = 0; + for (i = 32; i > 0; i--) + { + UINT32 nextstart = (curstart + bithisto[i]) >> 1; + if (i != 1 && nextstart * 2 != (curstart + bithisto[i])) + return HUFFERR_INTERNAL_INCONSISTENCY; + bithisto[i] = curstart; + curstart = nextstart; + } + + /* now assign canonical codes */ + for (i = 0; i < 256; i++) + { + huffman_node *node = &context->huffnode[i]; + if (node->numbits > 0) + node->bits = bithisto[node->numbits]++; + } + + /* if there was a decoding table, get rid of it now */ + context->lookupdirty = TRUE; + return HUFFERR_NONE; +} + + +/*------------------------------------------------- + huffman_build_lookup_table - build a lookup + table for fast decoding +-------------------------------------------------*/ + +static huffman_error huffman_build_lookup_table(huffman_context *context) +{ + int i; + + /* allocate a table if needed */ + if (context->lookup == NULL) + context->lookup = malloc((UINT32)sizeof(context->lookup[0]) * (UINT32)(1 << context->maxbits)); + if (context->lookup == NULL) + return HUFFERR_OUT_OF_MEMORY; + + /* now build */ + for (i = 0; i < 256; i++) + { + huffman_node *node = &context->huffnode[i]; + if (node->numbits > 0) + { + huffman_lookup_value *dest, *destend; + + /* left justify this node's bit values to max bits */ + int shift = context->maxbits - node->numbits; + UINT32 start = node->bits << shift; + UINT32 end = ((node->bits + 1) << shift) - 1; + huffman_lookup_value value; + + /* set up the entry */ + value = (i << 8) | node->numbits; + + /* fill all matching entries */ + dest = &context->lookup[start]; + destend = &context->lookup[end]; + while (dest <= destend) + *dest++ = value; + } + } + + /* no longer dirty */ + context->lookupdirty = FALSE; + return HUFFERR_NONE; +} diff --git a/src/lib/util/huffman.h b/src/lib/util/huffman.h new file mode 100644 index 00000000000..efc451b7483 --- /dev/null +++ b/src/lib/util/huffman.h @@ -0,0 +1,62 @@ +/*************************************************************************** + + huffman.h + + Huffman compression routines. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#ifndef __HUFFMAN_H__ + +#include "osdcore.h" + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +enum _huffman_error +{ + HUFFERR_NONE = 0, + HUFFERR_OUT_OF_MEMORY, + HUFFERR_TOO_MANY_BITS, + HUFFERR_INVALID_DATA, + HUFFERR_INPUT_BUFFER_TOO_SMALL, + HUFFERR_OUTPUT_BUFFER_TOO_SMALL, + HUFFERR_INTERNAL_INCONSISTENCY +}; +typedef enum _huffman_error huffman_error; + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +typedef UINT16 huffman_lookup_value; + +typedef struct _huffman_context huffman_context; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +huffman_error huffman_create_context(huffman_context **context, int maxbits); +void huffman_free_context(huffman_context *context); + +huffman_error huffman_compute_tree(huffman_context *context, const UINT8 *source, UINT32 slength, UINT32 sstride); +huffman_error huffman_import_tree(huffman_context *context, const UINT8 *source, UINT32 slength, UINT32 *actlength); +huffman_error huffman_export_tree(huffman_context *context, UINT8 *dest, UINT32 dlength, UINT32 *actlength); +huffman_error huffman_get_lookup_table(huffman_context *context, const huffman_lookup_value **table); + +huffman_error huffman_encode_data(huffman_context *context, const UINT8 *source, UINT32 slength, UINT8 *dest, UINT32 dlength, UINT32 *actlength); +huffman_error huffman_encode_data_interleaved_2(huffman_context *context1, huffman_context *context2, const UINT8 *source, UINT32 slength, UINT8 *dest, UINT32 dlength, UINT32 *actlength); +huffman_error huffman_decode_data(huffman_context *context, const UINT8 *source, UINT32 slength, UINT8 *dest, UINT32 dlength, UINT32 *actlength); +huffman_error huffman_decode_data_interleaved_2(huffman_context *context1, huffman_context *context2, const UINT8 *source, UINT32 slength, UINT8 *dest, UINT32 dlength, UINT32 *actlength); + +#endif diff --git a/src/lib/util/jedparse.c b/src/lib/util/jedparse.c new file mode 100644 index 00000000000..5a06b9353a5 --- /dev/null +++ b/src/lib/util/jedparse.c @@ -0,0 +1,435 @@ +/*************************************************************************** + + jedparse.c + + Parser for .JED files into raw fusemaps. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**************************************************************************** + + Binary file format: + + Offset + 0 = Total number of fuses (32 bits) + 4 = Raw fuse data, packed 8 bits at a time, LSB to MSB + +***************************************************************************/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include "jedparse.h" + + + +/*************************************************************************** + DEBUGGING +***************************************************************************/ + +#define LOG_PARSE 0 + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +typedef struct _parse_info parse_info; +struct _parse_info +{ + UINT16 checksum; /* checksum value */ + UINT32 explicit_numfuses; /* explicitly specified number of fuses */ +}; + + + +/*************************************************************************** + UTILITIES +***************************************************************************/ + +/*------------------------------------------------- + ishex - is a character a valid hex digit? +-------------------------------------------------*/ + +static int ishex(char c) +{ + return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F'); +} + + + +/*------------------------------------------------- + hexval - the hex value of a given character +-------------------------------------------------*/ + +static int hexval(char c) +{ + return (c >= '0' && c <= '9') ? (c - '0') : (10 + c - 'A'); +} + + + +/*------------------------------------------------- + isdelim - is a character a JEDEC delimiter? +-------------------------------------------------*/ + +static int isdelim(char c) +{ + return (c == ' ' || c == 13 || c == 10); +} + + + +/*------------------------------------------------- + suck_number - read a decimal value from the + character stream +-------------------------------------------------*/ + +static UINT32 suck_number(const UINT8 **psrc) +{ + const UINT8 *src = *psrc; + UINT32 value = 0; + + /* skip delimiters */ + while (isdelim(*src)) + src++; + + /* loop over and accumulate digits */ + while (isdigit(*src)) + { + value = value * 10 + *src - '0'; + src++; + } + + /* return a pointer to the string afterwards */ + *psrc = src; + return value; +} + + + +/*************************************************************************** + CORE IMPLEMENTATION +***************************************************************************/ + +/*------------------------------------------------- + process_field - process a single JEDEC field +-------------------------------------------------*/ + +static void process_field(jed_data *data, const UINT8 *cursrc, const UINT8 *srcend, parse_info *pinfo) +{ + /* switch off of the field type */ + switch (*cursrc) + { + case 'Q': + cursrc++; + switch (*cursrc) + { + /* number of fuses */ + case 'F': + cursrc++; + pinfo->explicit_numfuses = data->numfuses = suck_number(&cursrc); + break; + } + break; + + /* default fuse state (0 or 1) */ + case 'F': + cursrc++; + if (LOG_PARSE) printf("F%c\n", *cursrc); + if (*cursrc == '0') + memset(data->fusemap, 0x00, sizeof(data->fusemap)); + else + memset(data->fusemap, 0xff, sizeof(data->fusemap)); + break; + + /* fuse states */ + case 'L': + { + UINT32 curfuse; + + /* read the fuse number */ + cursrc++; + curfuse = suck_number(&cursrc); + if (LOG_PARSE) printf("L%d\n", curfuse); + + /* read digits, skipping delimiters */ + for ( ; cursrc < srcend; cursrc++) + if (*cursrc == '0' || *cursrc == '1') + { + jed_set_fuse(data, curfuse, *cursrc - '0'); + if (LOG_PARSE) printf(" fuse %d = %d\n", curfuse, 0); + if (curfuse >= data->numfuses) + data->numfuses = curfuse + 1; + curfuse++; + } + break; + } + + /* fuse checksum */ + case 'C': + cursrc++; + if (cursrc < srcend + 4 && ishex(cursrc[0]) && ishex(cursrc[1]) && ishex(cursrc[2]) && ishex(cursrc[3])) + { + pinfo->checksum = 0; + while (ishex(*cursrc) && cursrc < srcend) + pinfo->checksum = (pinfo->checksum << 4) | hexval(*cursrc++); + } + break; + } +} + + + +/*------------------------------------------------- + jed_parse - parse a .JED file that has been + loaded raw into memory +-------------------------------------------------*/ + +int jed_parse(const void *data, size_t length, jed_data *result) +{ + const UINT8 *cursrc = data; + const UINT8 *srcend = cursrc + length; + const UINT8 *scan; + parse_info pinfo; + UINT16 checksum; + int i; + + /* initialize the output and the intermediate info struct */ + memset(result, 0, sizeof(*result)); + memset(&pinfo, 0, sizeof(pinfo)); + + /* first scan for the STX character; ignore anything prior */ + while (cursrc < srcend && *cursrc != 0x02) + cursrc++; + if (cursrc >= srcend) + return JEDERR_INVALID_DATA; + + /* then scan to see if we have an ETX */ + scan = cursrc; + checksum = 0; + while (scan < srcend && *scan != 0x03) + checksum += *scan++ & 0x7f; + if (scan >= srcend) + return JEDERR_INVALID_DATA; + + /* see if there is a transmission checksum at the end */ + checksum += *scan; + if (scan + 4 < srcend && ishex(scan[1]) && ishex(scan[2]) && ishex(scan[3]) && ishex(scan[4])) + { + UINT16 dessum = (hexval(scan[1]) << 12) | (hexval(scan[2]) << 8) | (hexval(scan[3]) << 4) | hexval(scan[4] << 0); + if (dessum != 0 && dessum != checksum) + return JEDERR_BAD_XMIT_SUM; + } + + /* the ETX becomes the real srcend */ + srcend = scan; + + /* blast through the comment field */ + cursrc++; + while (cursrc < srcend && *cursrc != '*') + cursrc++; + + /* now loop over fields and decide which ones go in the file output */ + cursrc++; + while (cursrc < srcend) + { + /* skip over delimiters */ + while (cursrc < srcend && isdelim(*cursrc)) + cursrc++; + if (cursrc >= srcend) + break; + + /* end of field is an asterisk -- find it */ + scan = cursrc; + while (scan < srcend && *scan != '*') + scan++; + if (scan >= srcend) + return JEDERR_INVALID_DATA; + + /* process the field */ + process_field(result, cursrc, scan, &pinfo); + + /* advance past it */ + cursrc = scan + 1; + } + + /* if we got an explicit fuse count, override our computed count */ + if (pinfo.explicit_numfuses != 0) + result->numfuses = pinfo.explicit_numfuses; + + /* clear out leftover bits */ + if (result->numfuses % 8 != 0) + result->fusemap[result->numfuses / 8] &= (1 << (result->numfuses % 8)) - 1; + memset(&result->fusemap[(result->numfuses + 7) / 8], 0, sizeof(result->fusemap) - (result->numfuses + 7) / 8); + + /* validate the checksum */ + checksum = 0; + for (i = 0; i < (result->numfuses + 7) / 8; i++) + checksum += result->fusemap[i]; + if (pinfo.checksum != 0 && checksum != pinfo.checksum) + return JEDERR_BAD_FUSE_SUM; + + return JEDERR_NONE; +} + + + +/*------------------------------------------------- + jed_output - generate a new .JED file based + on the jed_data provided +-------------------------------------------------*/ + +size_t jed_output(const jed_data *data, void *result, size_t length) +{ + UINT8 *curdst = result; + UINT8 *dstend = curdst + length; + int i, zeros, ones; + char tempbuf[256]; + UINT16 checksum; + UINT8 defbyte; + UINT8 *temp; + + /* always start the DST with a standard header and an STX */ + tempbuf[0] = 0x02; + sprintf(&tempbuf[1], "JEDEC file generated by jedutil*\n"); + if (curdst + strlen(tempbuf) <= dstend) + memcpy(curdst, tempbuf, strlen(tempbuf)); + curdst += strlen(tempbuf); + + /* append the package information */ + sprintf(tempbuf, "QF%d*\n", data->numfuses); + if (curdst + strlen(tempbuf) <= dstend) + memcpy(curdst, tempbuf, strlen(tempbuf)); + curdst += strlen(tempbuf); + + /* compute the checksum */ + checksum = 0; + for (i = 0; i < data->numfuses / 8; i++) + checksum += data->fusemap[i]; + if (data->numfuses % 8 != 0) + checksum += data->fusemap[data->numfuses / 8] & ((1 << (data->numfuses % 8)) - 1); + + /* determine if we are mostly 0's or mostly 1's */ + for (i = zeros = ones = 0; i < data->numfuses / 8; i++) + if (data->fusemap[i] == 0x00) + zeros++; + else if (data->fusemap[i] == 0xff) + ones++; + defbyte = (ones > zeros) ? 0xff : 0x00; + + /* output the default fuse state */ + sprintf(tempbuf, "F%d*\n", defbyte & 1); + if (curdst + strlen(tempbuf) <= dstend) + memcpy(curdst, tempbuf, strlen(tempbuf)); + curdst += strlen(tempbuf); + + /* now loop over groups of 32 fuses and output non-default groups */ + for (i = 0; i < data->numfuses; i += 32) + if (data->fusemap[i / 8 + 0] != defbyte || + data->fusemap[i / 8 + 1] != defbyte || + data->fusemap[i / 8 + 2] != defbyte || + data->fusemap[i / 8 + 3] != defbyte) + { + int stroffs; + int j; + + /* build up a string of 32 fuses */ + stroffs = sprintf(tempbuf, "L%05d ", i); + for (j = 0; j < 32 && i+j < data->numfuses; j++) + tempbuf[stroffs++] = '0' + jed_get_fuse(data, i + j); + stroffs += sprintf(&tempbuf[stroffs], "*\n"); + + /* append to the buffer */ + if (curdst + strlen(tempbuf) <= dstend) + memcpy(curdst, tempbuf, strlen(tempbuf)); + curdst += strlen(tempbuf); + } + + /* write the checksum */ + sprintf(tempbuf, "C%04X*\n", checksum); + if (curdst + strlen(tempbuf) <= dstend) + memcpy(curdst, tempbuf, strlen(tempbuf)); + curdst += strlen(tempbuf); + + /* now compute the transmission checksum */ + checksum = 0; + for (temp = result; temp < curdst && temp < dstend; temp++) + checksum += *temp & 0x7f; + checksum += 0x03; + + /* append the ETX and the transmission checksum */ + tempbuf[0] = 0x03; + sprintf(&tempbuf[1], "%04X", checksum); + if (curdst + strlen(tempbuf) <= dstend) + memcpy(curdst, tempbuf, strlen(tempbuf)); + curdst += strlen(tempbuf); + + /* return the final size */ + return curdst - (UINT8 *)result; +} + + + +/*------------------------------------------------- + jedbin_parse - parse a binary JED file that + has been loaded raw into memory +-------------------------------------------------*/ + +int jedbin_parse(const void *data, size_t length, jed_data *result) +{ + const UINT8 *cursrc = data; + + /* initialize the output */ + memset(result, 0, sizeof(*result)); + + /* need at least 4 bytes */ + if (length < 4) + return JEDERR_INVALID_DATA; + + /* first unpack the number of fuses */ + result->numfuses = (cursrc[0] << 24) | (cursrc[1] << 16) | (cursrc[2] << 8) | cursrc[3]; + cursrc += 4; + if (result->numfuses == 0 || result->numfuses > JED_MAX_FUSES) + return JEDERR_INVALID_DATA; + + /* now make sure we have enough data in the source */ + if (length < 4 + (result->numfuses + 7) / 8) + return JEDERR_INVALID_DATA; + + /* copy in the data */ + memcpy(result->fusemap, cursrc, (result->numfuses + 7) / 8); + return JEDERR_NONE; +} + + + +/*------------------------------------------------- + jedbin_output - generate a new binary JED file + based on the jed_data provided +-------------------------------------------------*/ + +size_t jedbin_output(const jed_data *data, void *result, size_t length) +{ + UINT8 *curdst = result; + + /* ensure we have enough room */ + if (length >= 4 + (data->numfuses + 7) / 8) + { + /* store the number of fuses */ + *curdst++ = data->numfuses >> 24; + *curdst++ = data->numfuses >> 16; + *curdst++ = data->numfuses >> 8; + *curdst++ = data->numfuses >> 0; + + /* copy in the rest of the data */ + memcpy(curdst, data->fusemap, (data->numfuses + 7) / 8); + } + + /* return the final size */ + return 4 + (data->numfuses + 7) / 8; +} + diff --git a/src/lib/util/jedparse.h b/src/lib/util/jedparse.h new file mode 100644 index 00000000000..4697914cfac --- /dev/null +++ b/src/lib/util/jedparse.h @@ -0,0 +1,89 @@ +/*************************************************************************** + + jedparse.h + + Parser for .JED files into raw fusemaps. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#ifndef __JEDPARSE_H__ +#define __JEDPARSE_H__ + +#include "osdcore.h" + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define JED_MAX_FUSES (65536) + +/* error codes */ +#define JEDERR_NONE 0 +#define JEDERR_INVALID_DATA 1 +#define JEDERR_BAD_XMIT_SUM 2 +#define JEDERR_BAD_FUSE_SUM 3 + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +typedef struct _jed_data jed_data; +struct _jed_data +{ + UINT32 numfuses; /* number of defined fuses */ + UINT8 fusemap[JED_MAX_FUSES / 8];/* array of bit-packed data */ +}; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +/* parse a file (read into memory) into a jed_data structure */ +int jed_parse(const void *data, size_t length, jed_data *result); + +/* output a jed_data structure into a well-formatted JED file */ +size_t jed_output(const jed_data *data, void *result, size_t length); + +/* parse a binary JED file (read into memory) into a jed_data structure */ +int jedbin_parse(const void *data, size_t length, jed_data *result); + +/* output a jed_data structure into a binary JED file */ +size_t jedbin_output(const jed_data *data, void *result, size_t length); + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +INLINE int jed_get_fuse(const jed_data *data, UINT32 fusenum) +{ + if (fusenum < JED_MAX_FUSES) + return (data->fusemap[fusenum / 8] >> (fusenum % 8)) & 1; + else + return 0; +} + + +INLINE void jed_set_fuse(jed_data *data, UINT32 fusenum, UINT8 value) +{ + if (fusenum < JED_MAX_FUSES) + { + /* set or clear the bit as appropriate */ + if (value) + data->fusemap[fusenum / 8] |= (1 << (fusenum % 8)); + else + data->fusemap[fusenum / 8] &= ~(1 << (fusenum % 8)); + } +} + +#endif /* __JEDPARSE_H__ */ diff --git a/src/lib/util/md5.c b/src/lib/util/md5.c new file mode 100644 index 00000000000..28797aa811c --- /dev/null +++ b/src/lib/util/md5.c @@ -0,0 +1,239 @@ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + * Changed so as no longer to depend on Colin Plumb's `usual.h' header + * definitions; now uses stuff from dpkg's config.h. + * - Ian Jackson <ijackson@nyx.cs.du.edu>. + * Still in the public domain. + */ + +#include <string.h> /* for memcpy() */ + +#include "md5.h" + +#ifndef LSB_FIRST +void +byteSwap(UWORD32 *buf, unsigned words) +{ + md5byte *p = (md5byte *)buf; + + do { + *buf++ = (UWORD32)((unsigned)p[3] << 8 | p[2]) << 16 | + ((unsigned)p[1] << 8 | p[0]); + p += 4; + } while (--words); +} +#else +#define byteSwap(buf,words) +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void +MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bytes[0] = 0; + ctx->bytes[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void +MD5Update(struct MD5Context *ctx, md5byte const *buf, unsigned len) +{ + UWORD32 t; + + /* Update byte count */ + + t = ctx->bytes[0]; + if ((ctx->bytes[0] = t + len) < t) + ctx->bytes[1]++; /* Carry from low to high */ + + t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */ + if (t > len) { + memcpy((md5byte *)ctx->in + 64 - t, buf, len); + return; + } + /* First chunk is an odd size */ + memcpy((md5byte *)ctx->in + 64 - t, buf, t); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + buf += t; + len -= t; + + /* Process data in 64-byte chunks */ + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void +MD5Final(md5byte digest[16], struct MD5Context *ctx) +{ + int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */ + md5byte *p = (md5byte *)ctx->in + count; + + /* Set the first char of padding to 0x80. There is always room. */ + *p++ = 0x80; + + /* Bytes of padding needed to make 56 bytes (-8..55) */ + count = 56 - 1 - count; + + if (count < 0) { /* Padding forces an extra block */ + memset(p, 0, count + 8); + byteSwap(ctx->in, 16); + MD5Transform(ctx->buf, ctx->in); + p = (md5byte *)ctx->in; + count = 56; + } + memset(p, 0, count); + byteSwap(ctx->in, 14); + + /* Append length in bits and transform */ + ctx->in[14] = ctx->bytes[0] << 3; + ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; + MD5Transform(ctx->buf, ctx->in); + + byteSwap(ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +#ifndef ASM_MD5 + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f,w,x,y,z,in,s) \ + (w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +void +MD5Transform(UWORD32 buf[4], UWORD32 const in[16]) +{ + register UWORD32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +#endif + diff --git a/src/lib/util/md5.h b/src/lib/util/md5.h new file mode 100644 index 00000000000..4a41494e718 --- /dev/null +++ b/src/lib/util/md5.h @@ -0,0 +1,42 @@ +/* + * This is the header file for the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + * + * Changed so as no longer to depend on Colin Plumb's `usual.h' + * header definitions; now uses stuff from dpkg's config.h + * - Ian Jackson <ijackson@nyx.cs.du.edu>. + * Still in the public domain. + */ + +#ifndef MD5_H +#define MD5_H + +typedef unsigned int UWORD32; + +#define md5byte unsigned char + +struct MD5Context { + UWORD32 buf[4]; + UWORD32 bytes[2]; + UWORD32 in[16]; +}; + +void MD5Init(struct MD5Context *context); +void MD5Update(struct MD5Context *context, md5byte const *buf, unsigned len); +void MD5Final(unsigned char digest[16], struct MD5Context *context); +void MD5Transform(UWORD32 buf[4], UWORD32 const in[16]); + +#endif /* !MD5_H */ + diff --git a/src/lib/util/options.c b/src/lib/util/options.c new file mode 100644 index 00000000000..44534e2247d --- /dev/null +++ b/src/lib/util/options.c @@ -0,0 +1,1133 @@ +/*************************************************************************** + + options.c + + Core options code code + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#include <stdarg.h> +#include <ctype.h> +#include "options.h" +#include "astring.h" + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define MAX_ENTRY_NAMES 4 + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* forward references */ +typedef struct _options_data options_data; + + +/* range parameter for minimum/maximum values */ +typedef union _options_range_parameter options_range_parameter; +union _options_range_parameter +{ + float f; + int i; +}; + + +/* hash table entries */ +typedef struct _options_hash_entry options_hash_entry; +struct _options_hash_entry +{ + options_hash_entry * next; /* link to the next entry */ + astring * name; /* name under the current link */ + options_data * data; /* link to associated options_data */ +}; + + +/* information about a single entry in the options */ +struct _options_data +{ + options_hash_entry links[MAX_ENTRY_NAMES]; /* array of hash table entries (one per name) */ + options_data * next; /* link to the next data */ + UINT32 flags; /* flags from the entry */ + UINT32 seqid; /* sequence ID; bumped on each change */ + int error_reported; /* have we reported an error on this option yet? */ + int priority; /* priority of the data set */ + astring * data; /* data for this item */ + astring * defdata; /* default data for this item */ + const char * description; /* description for this item */ + options_range_type range_type; /* the type of range to apply to this item */ + options_range_parameter range_minimum; /* the minimum of the range */ + options_range_parameter range_maximum; /* the maximum of the range */ + void (*callback)(core_options *opts, const char *arg); /* callback to be invoked when parsing */ +}; + + +/* structure holding information about a collection of options */ +struct _core_options +{ + void (*output[OPTMSG_COUNT])(const char *s);/* output callbacks */ + + /* linked list, for sequential iteration */ + options_data * datalist; /* linked list for sequential iteration */ + options_data ** datalist_nextptr; /* pointer to pointer to tail entry */ + + /* hashtable, for fast lookup */ + options_hash_entry * hashtable[101]; /* hash table for fast lookup */ +}; + + +/* information about an in-progress options enumeration */ +struct _options_enumerator +{ + options_data * current; +}; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +static options_data *find_entry_data(core_options *opts, const char *string, int is_command_line); +static void update_data(core_options *opts, options_data *data, const char *newdata, int priority); +static int parse_option_name(core_options *opts, const char *srcstring, options_data *data); + +static void message(core_options *opts, options_message msgtype, const char *format, ...); +static UINT32 hash_value(core_options *opts, const char *str); +static void output_printf(void (*output)(const char *s), const char *format, ...); + + + +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +const char *option_unadorned[MAX_UNADORNED_OPTIONS] = +{ + "<UNADORNED0>", + "<UNADORNED1>", + "<UNADORNED2>", + "<UNADORNED3>", + "<UNADORNED4>", + "<UNADORNED5>", + "<UNADORNED6>", + "<UNADORNED7>", + "<UNADORNED8>", + "<UNADORNED9>", + "<UNADORNED10>", + "<UNADORNED11>", + "<UNADORNED12>", + "<UNADORNED13>", + "<UNADORNED14>", + "<UNADORNED15>" +}; + + + +/*************************************************************************** + OPTIONS COLLECTION MANAGEMENT +***************************************************************************/ + +/*------------------------------------------------- + options_create - creates a new instance of + core options +-------------------------------------------------*/ + +core_options *options_create(void (*fail)(const char *message)) +{ + /* allocate memory for the option block */ + core_options *opts = (core_options *)malloc(sizeof(*opts)); + if (opts == NULL) + goto error; + + /* and set up the structure */ + memset(opts, 0, sizeof(*opts)); + opts->datalist_nextptr = &opts->datalist; + return opts; + +error: + return NULL; +} + + +/*------------------------------------------------- + options_free - frees an options object +-------------------------------------------------*/ + +void options_free(core_options *opts) +{ + options_data *data, *next; + + /* loop over data items and free them */ + for (data = opts->datalist; data != NULL; data = next) + { + int linknum; + + next = data->next; + + /* free names */ + for (linknum = 0; linknum < ARRAY_LENGTH(data->links); linknum++) + if (data->links[linknum].name != NULL) + astring_free(data->links[linknum].name); + + /* free strings */ + astring_free(data->data); + astring_free(data->defdata); + + /* free the data itself */ + free(data); + } + + /* free the options itself */ + free(opts); +} + + +/*------------------------------------------------- + options_set_output_callback - installs a callback + to be invoked when data is outputted in the + process of outputting options +-------------------------------------------------*/ + +void options_set_output_callback(core_options *opts, options_message msgtype, void (*callback)(const char *s)) +{ + opts->output[msgtype] = callback; +} + + +/*------------------------------------------------- + options_revert - revert options at or below + a certain priority back to their defaults +-------------------------------------------------*/ + +void options_revert(core_options *opts, int priority) +{ + options_data *data; + + /* iterate over options and revert to defaults if below the given priority */ + for (data = opts->datalist; data != NULL; data = data->next) + if (data->priority <= priority) + { + astring_cpy(data->data, data->defdata); + data->priority = OPTION_PRIORITY_DEFAULT; + } +} + + +/*------------------------------------------------- + options_copy - copy options from one core_options + to another +-------------------------------------------------*/ + +int options_copy(core_options *dest_opts, core_options *src_opts) +{ + options_data *data; + + /* iterate over options in the destination */ + for (data = dest_opts->datalist; data != NULL; data = data->next) + if (!(data->flags & OPTION_HEADER)) + { + options_data *srcdata = find_entry_data(src_opts, astring_c(data->links[0].name), FALSE); + + /* if the option exists in the source, set it in the destination */ + if (srcdata != NULL) + options_set_string(dest_opts, astring_c(srcdata->links[0].name), astring_c(srcdata->data), srcdata->priority); + } + + return TRUE; +} + + +/*------------------------------------------------- + options_equal - compare two sets of options +-------------------------------------------------*/ + +int options_equal(core_options *opts1, core_options *opts2) +{ + options_data *data; + + /* iterate over options in the first list */ + for (data = opts1->datalist; data != NULL; data = data->next) + if (!(data->flags & OPTION_HEADER)) + { + const char *value1 = options_get_string(opts1, astring_c(data->links[0].name)); + const char *value2 = options_get_string(opts2, astring_c(data->links[0].name)); + + /* if the values differ, return false */ + if (strcmp(value1, value2) != 0) + return FALSE; + } + + return TRUE; +} + + + +/*************************************************************************** + OPTION DEFINITIONS +***************************************************************************/ + +/*------------------------------------------------- + options_add_entries - add entries to the + current options sets +-------------------------------------------------*/ + +int options_add_entries(core_options *opts, const options_entry *entrylist) +{ + /* loop over entries until we hit a NULL name */ + for ( ; entrylist->name != NULL || (entrylist->flags & OPTION_HEADER); entrylist++) + { + options_data *match = NULL; + int i; + + /* allocate a new item */ + options_data *data = malloc(sizeof(*data)); + if (data == NULL) + return FALSE; + memset(data, 0, sizeof(*data)); + + /* parse the option name */ + if (entrylist->name != NULL) + parse_option_name(opts, entrylist->name, data); + + /* do we match an existing entry? */ + for (i = 0; i < ARRAY_LENGTH(data->links) && match == NULL; i++) + if (data->links[i].name != NULL) + match = find_entry_data(opts, astring_c(data->links[i].name), FALSE); + + /* if so, throw away this entry and replace the data */ + if (match != NULL) + { + /* free what we've allocated so far */ + for (i = 0; i < ARRAY_LENGTH(data->links); i++) + if (data->links[i].name != NULL) + astring_free(data->links[i].name); + free(data); + + /* use the matching entry as our data */ + data = match; + } + + /* otherwise, finish making the new entry */ + else + { + /* allocate strings */ + data->data = astring_alloc(); + data->defdata = astring_alloc(); + + /* copy the flags, and set the value equal to the default */ + data->flags = entrylist->flags; + data->description = entrylist->description; + + /* add us to the end of the sequential list */ + *opts->datalist_nextptr = data; + opts->datalist_nextptr = &data->next; + + /* add each name to the appropriate hash table with a link back to us */ + for (i = 0; i < ARRAY_LENGTH(data->links); i++) + if (data->links[i].name != NULL) + { + int hash_entry = hash_value(opts, astring_c(data->links[i].name)); + + /* set up link */ + data->links[i].data = data; + data->links[i].next = opts->hashtable[hash_entry]; + opts->hashtable[hash_entry] = &data->links[i]; + } + } + + /* copy in the data and default data values */ + if (entrylist->defvalue != NULL) + { + astring_cpyc(data->data, entrylist->defvalue); + astring_cpyc(data->defdata, entrylist->defvalue); + } + data->priority = OPTION_PRIORITY_DEFAULT; + } + return TRUE; +} + + +/*------------------------------------------------- + options_set_option_default_value - change the + default value of an option +-------------------------------------------------*/ + +int options_set_option_default_value(core_options *opts, const char *name, const char *defvalue) +{ + options_data *data = find_entry_data(opts, name, TRUE); + + /* if we don't have an entry for this, fail */ + if (data == NULL) + return FALSE; + + /* update the data and default data; note that we assume that data == defdata */ + astring_cpyc(data->data, defvalue); + astring_cpyc(data->defdata, defvalue); + data->priority = OPTION_PRIORITY_DEFAULT; + return TRUE; +} + + +/*------------------------------------------------- + options_set_option_callback - specifies a + callback to be invoked when parsing options +-------------------------------------------------*/ + +int options_set_option_callback(core_options *opts, const char *name, void (*callback)(core_options *opts, const char *arg)) +{ + options_data *data = find_entry_data(opts, name, TRUE); + + /* if we don't find an entry, fail */ + if (data == NULL) + return FALSE; + + /* set the callback */ + data->callback = callback; + return TRUE; +} + + + +/*************************************************************************** + OPTION DATA EXTRACTION +***************************************************************************/ + +/*------------------------------------------------- + options_parse_command_line - parse a series + of command line arguments +-------------------------------------------------*/ + +int options_parse_command_line(core_options *opts, int argc, char **argv, int priority) +{ + int unadorned_index = 0; + int arg; + + /* loop over commands, looking for options */ + for (arg = 1; arg < argc; arg++) + { + const char *optionname, *newdata; + options_data *data; + int is_unadorned; + + /* determine the entry name to search for */ + is_unadorned = (argv[arg][0] != '-'); + if (!is_unadorned) + optionname = &argv[arg][1]; + else + optionname = OPTION_UNADORNED(unadorned_index); + + /* find our entry */ + data = find_entry_data(opts, optionname, TRUE); + if (data == NULL) + { + message(opts, OPTMSG_ERROR, "Error: unknown option: %s\n", argv[arg]); + return 1; + } + + /* if unadorned, we have to bump the count (unless if the option repeats) */ + if (is_unadorned && !(data->flags & OPTION_REPEATS)) + unadorned_index++; + + /* get the data for this argument, special casing booleans */ + if ((data->flags & (OPTION_BOOLEAN | OPTION_COMMAND)) != 0) + newdata = (strncmp(&argv[arg][1], "no", 2) == 0) ? "0" : "1"; + else if (argv[arg][0] != '-') + newdata = argv[arg]; + else if (arg + 1 < argc) + newdata = argv[++arg]; + else + { + message(opts, OPTMSG_ERROR, "Error: option %s expected a parameter\n", argv[arg]); + return 1; + } + + /* if the option is deprecated or internal, don't process further */ + if ((data->flags & (OPTION_DEPRECATED | OPTION_INTERNAL)) != 0) + continue; + + /* invoke callback, if present */ + if (data->callback != NULL) + (*data->callback)(opts, newdata); + + /* allocate a new copy of data for this */ + update_data(opts, data, newdata, priority); + } + return 0; +} + + +/*------------------------------------------------- + options_parse_ini_file - parse a series + of entries in an INI file +-------------------------------------------------*/ + +int options_parse_ini_file(core_options *opts, core_file *inifile, int priority) +{ + char buffer[4096]; + + /* loop over data */ + while (core_fgets(buffer, ARRAY_LENGTH(buffer), inifile) != NULL) + { + char *optionname, *optiondata, *temp; + options_data *data; + int inquotes = FALSE; + + /* find the name */ + for (optionname = buffer; *optionname != 0; optionname++) + if (!isspace(*optionname)) + break; + + /* skip comments */ + if (*optionname == 0 || *optionname == '#') + continue; + + /* scan forward to find the first space */ + for (temp = optionname; *temp != 0; temp++) + if (isspace(*temp)) + break; + + /* if we hit the end early, print a warning and continue */ + if (*temp == 0) + { + message(opts, OPTMSG_WARNING, "Warning: invalid line in INI: %s", buffer); + continue; + } + + /* NULL-terminate */ + *temp++ = 0; + optiondata = temp; + + /* scan the data, stopping when we hit a comment */ + for (temp = optiondata; *temp != 0; temp++) + { + if (*temp == '"') + inquotes = !inquotes; + if (*temp == '#' && !inquotes) + break; + } + *temp = 0; + + /* find our entry */ + data = find_entry_data(opts, optionname, FALSE); + if (data == NULL) + { + message(opts, OPTMSG_WARNING, "Warning: unknown option in INI: %s\n", optionname); + continue; + } + if ((data->flags & (OPTION_DEPRECATED | OPTION_INTERNAL)) != 0) + continue; + + /* allocate a new copy of data for this */ + update_data(opts, data, optiondata, priority); + } + return 0; +} + + + +/*************************************************************************** + OPTIONS OUTPUT +***************************************************************************/ + +/*------------------------------------------------- + options_output_diff_ini_file - output the diff + of the current state from a base state to an + INI file +-------------------------------------------------*/ + +void options_output_diff_ini_file(core_options *opts, core_options *baseopts, core_file *inifile) +{ + options_data *data; + const char *last_header = NULL; + const char *name; + const char *value; + options_data *basedata; + + /* loop over all items */ + for (data = opts->datalist; data != NULL; data = data->next) + { + /* header: record description */ + if ((data->flags & OPTION_HEADER) != 0) + last_header = data->description; + + /* otherwise, output entries for all non-deprecated and non-command items (if not in baseopts) */ + else if ((data->flags & (OPTION_DEPRECATED | OPTION_INTERNAL | OPTION_COMMAND)) == 0) + { + /* get name and data of this value */ + name = astring_c(data->links[0].name); + value = astring_c(data->data); + + /* look up counterpart in baseopts, if baseopts is specified */ + basedata = (baseopts != NULL) ? find_entry_data(baseopts, name, FALSE) : NULL; + + /* is our data different, or not in baseopts? */ + if ((basedata == NULL) || (strcmp(value, astring_c(basedata->data)) != 0)) + { + /* output header, if we have one */ + if (last_header != NULL) + { + core_fprintf(inifile, "\n#\n# %s\n#\n", last_header); + last_header = NULL; + } + + /* and finally output the data */ + if (strchr(value, ' ') != NULL) + core_fprintf(inifile, "%-25s \"%s\"\n", name, value); + else + core_fprintf(inifile, "%-25s %s\n", name, value); + } + } + } +} + + +/*------------------------------------------------- + options_output_ini_file - output the current + state to an INI file +-------------------------------------------------*/ + +void options_output_ini_file(core_options *opts, core_file *inifile) +{ + options_output_diff_ini_file(opts, NULL, inifile); +} + + +/*------------------------------------------------- + options_output_ini_file - output the current + state to an INI file +-------------------------------------------------*/ + +void options_output_ini_stdfile(core_options *opts, FILE *inifile) +{ + options_data *data; + + /* loop over all items */ + for (data = opts->datalist; data != NULL; data = data->next) + { + /* header: just print */ + if ((data->flags & OPTION_HEADER) != 0) + fprintf(inifile, "\n#\n# %s\n#\n", data->description); + + /* otherwise, output entries for all non-deprecated and non-command items */ + else if ((data->flags & (OPTION_DEPRECATED | OPTION_INTERNAL | OPTION_COMMAND)) == 0) + { + if (astring_chr(data->data, 0, ' ') != -1) + fprintf(inifile, "%-25s \"%s\"\n", astring_c(data->links[0].name), astring_c(data->data)); + else + fprintf(inifile, "%-25s %s\n", astring_c(data->links[0].name), astring_c(data->data)); + } + } +} + + +/*------------------------------------------------- + options_output_help - output option help to + a file +-------------------------------------------------*/ + +void options_output_help(core_options *opts, void (*output)(const char *)) +{ + options_data *data; + + /* loop over all items */ + for (data = opts->datalist; data != NULL; data = data->next) + { + /* header: just print */ + if ((data->flags & OPTION_HEADER) != 0) + output_printf(output, "\n#\n# %s\n#\n", data->description); + + /* otherwise, output entries for all non-deprecated items */ + else if ((data->flags & (OPTION_DEPRECATED | OPTION_INTERNAL)) == 0 && data->description != NULL) + output_printf(output, "-%-20s%s\n", astring_c(data->links[0].name), data->description); + } +} + + + +/*************************************************************************** + OPTIONS READING +***************************************************************************/ + +/*------------------------------------------------- + options_get_string - return data formatted + as a string +-------------------------------------------------*/ + +const char *options_get_string(core_options *opts, const char *name) +{ + options_data *data = find_entry_data(opts, name, FALSE); + const char *value = ""; + + /* error if not found */ + if (data == NULL) + message(opts, OPTMSG_ERROR, "Unexpected option %s queried\n", name); + + /* copy if non-NULL */ + else + value = astring_c(data->data); + + return value; +} + + +/*------------------------------------------------- + options_get_bool - return data formatted as + a boolean +-------------------------------------------------*/ + +int options_get_bool(core_options *opts, const char *name) +{ + options_data *data = find_entry_data(opts, name, FALSE); + int value = FALSE; + + /* error if not found */ + if (data == NULL) + message(opts, OPTMSG_ERROR, "Unexpected boolean option %s queried\n", name); + + /* also error if we don't have a valid boolean value */ + else if (sscanf(astring_c(data->data), "%d", &value) != 1 || value < 0 || value > 1) + { + options_set_string(opts, name, astring_c(data->defdata), 0); + sscanf(astring_c(data->data), "%d", &value); + if (!data->error_reported) + { + message(opts, OPTMSG_ERROR, "Illegal boolean value for %s; reverting to %d\n", astring_c(data->links[0].name), value); + data->error_reported = TRUE; + } + } + return value; +} + + +/*------------------------------------------------- + options_get_int - return data formatted as + an integer +-------------------------------------------------*/ + +int options_get_int(core_options *opts, const char *name) +{ + options_data *data = find_entry_data(opts, name, FALSE); + int value = 0; + + /* error if not found */ + if (data == NULL) + message(opts, OPTMSG_ERROR, "Unexpected integer option %s queried\n", name); + + /* also error if we don't have a valid integer value */ + else if (sscanf(astring_c(data->data), "%d", &value) != 1) + { + options_set_string(opts, name, astring_c(data->defdata), 0); + sscanf(astring_c(data->data), "%d", &value); + if (!data->error_reported) + { + message(opts, OPTMSG_ERROR, "Illegal integer value for %s; reverting to %d\n", astring_c(data->links[0].name), value); + data->error_reported = TRUE; + } + } + return value; +} + + +/*------------------------------------------------- + options_get_float - return data formatted as + a float +-------------------------------------------------*/ + +float options_get_float(core_options *opts, const char *name) +{ + options_data *data = find_entry_data(opts, name, FALSE); + float value = 0; + + /* error if not found */ + if (data == NULL) + message(opts, OPTMSG_ERROR, "Unexpected float option %s queried\n", name); + + /* also error if we don't have a valid floating point value */ + else if (sscanf(astring_c(data->data), "%f", &value) != 1) + { + options_set_string(opts, name, astring_c(data->defdata), 0); + sscanf(astring_c(data->data), "%f", &value); + if (!data->error_reported) + { + message(opts, OPTMSG_ERROR, "Illegal float value for %s; reverting to %f\n", astring_c(data->links[0].name), (double)value); + data->error_reported = TRUE; + } + } + return value; +} + + +/*------------------------------------------------- + options_get_seqid - return the seqid for an + entry +-------------------------------------------------*/ + +UINT32 options_get_seqid(core_options *opts, const char *name) +{ + options_data *data = find_entry_data(opts, name, FALSE); + return (data == NULL) ? 0 : data->seqid; +} + + + +/*************************************************************************** + OPTIONS SETTING +***************************************************************************/ + +/*------------------------------------------------- + options_set_string - set a string value +-------------------------------------------------*/ + +void options_set_string(core_options *opts, const char *name, const char *value, int priority) +{ + options_data *data = find_entry_data(opts, name, FALSE); + update_data(opts, data, value, priority); +} + + +/*------------------------------------------------- + options_set_bool - set a boolean value +-------------------------------------------------*/ + +void options_set_bool(core_options *opts, const char *name, int value, int priority) +{ + char temp[4]; + sprintf(temp, "%d", value ? 1 : 0); + options_set_string(opts, name, temp, priority); +} + + +/*------------------------------------------------- + options_set_int - set an integer value +-------------------------------------------------*/ + +void options_set_int(core_options *opts, const char *name, int value, int priority) +{ + char temp[20]; + sprintf(temp, "%d", value); + options_set_string(opts, name, temp, priority); +} + + +/*------------------------------------------------- + options_set_float - set a float value +-------------------------------------------------*/ + +void options_set_float(core_options *opts, const char *name, float value, int priority) +{ + char temp[100]; + sprintf(temp, "%f", value); + options_set_string(opts, name, temp, priority); +} + + + +/*************************************************************************** + OPTION DEFINITION QUERIES +***************************************************************************/ + +/*------------------------------------------------- + options_enumerator_begin - retrieve the range of + a float option +-------------------------------------------------*/ + +options_enumerator *options_enumerator_begin(core_options *opts) +{ + options_enumerator *enumerator; + + /* allocate memory for the enumerator */ + enumerator = malloc(sizeof(*enumerator)); + if (enumerator == NULL) + return NULL; + + /* start at the head of the list */ + enumerator->current = opts->datalist; + return enumerator; +} + + + +/*------------------------------------------------- + options_enumerator_next - returns the current + option and advances the enumerator +-------------------------------------------------*/ + +const char *options_enumerator_next(options_enumerator *enumerator) +{ + astring *option_name = NULL; + + /* be sure to skip over false options */ + while (option_name == NULL && enumerator->current != NULL) + { + /* retrieve the current option name and advance the enumerator */ + option_name = enumerator->current->links[0].name; + enumerator->current = enumerator->current->next; + } + return (option_name != NULL) ? astring_c(option_name) : NULL; +} + + + +/*------------------------------------------------- + options_enumerator_free - disposes an options + enumerator +-------------------------------------------------*/ + +void options_enumerator_free(options_enumerator *enumerator) +{ + free(enumerator); +} + + +/*------------------------------------------------- + options_get_range_type - determine the type + of range for a particular option +-------------------------------------------------*/ + +options_range_type options_get_range_type(core_options *opts, const char *name) +{ + options_data *data = find_entry_data(opts, name, FALSE); + return data->range_type; +} + + +/*------------------------------------------------- + options_get_range_int - retrieve the range of + an integer option +-------------------------------------------------*/ + +void options_get_range_int(core_options *opts, const char *name, int *minval, int *maxval) +{ + options_data *data = find_entry_data(opts, name, FALSE); + *minval = data->range_minimum.i; + *maxval = data->range_maximum.i; +} + + + +/*------------------------------------------------- + options_get_range_float - retrieve the range of + a float option +-------------------------------------------------*/ + +void options_get_range_float(core_options *opts, const char *name, float *minval, float *maxval) +{ + options_data *data = find_entry_data(opts, name, FALSE); + *minval = data->range_minimum.f; + *maxval = data->range_maximum.f; +} + + + +/*************************************************************************** + INTERNAL UTILITIES +***************************************************************************/ + +/*------------------------------------------------- + find_entry_data - locate an entry whose name + matches the given string +-------------------------------------------------*/ + +static options_data *find_entry_data(core_options *opts, const char *string, int is_command_line) +{ + int hash_entry = hash_value(opts, string); + options_hash_entry *link; + + /* scan all entries */ + for (link = opts->hashtable[hash_entry]; link != NULL; link = link->next) + if (!(link->data->flags & OPTION_HEADER) && link->name != NULL && astring_cmpc(link->name, string) == 0) + return link->data; + + /* haven't found it? if we are prefixed with "no", then try to search for that */ + if (is_command_line && string[0] == 'n' && string[1] == 'o') + { + options_data *data = find_entry_data(opts, &string[2], FALSE); + if (data != NULL && (data->flags & OPTION_BOOLEAN)) + return data; + } + + /* didn't find it at all */ + return NULL; +} + + +/*------------------------------------------------- + update_data - update the data value for a + given entry +-------------------------------------------------*/ + +static void update_data(core_options *opts, options_data *data, const char *newdata, int priority) +{ + const char *dataend = newdata + strlen(newdata) - 1; + const char *datastart = newdata; + float f; + int i; + + /* strip off leading/trailing spaces */ + while (isspace(*datastart) && datastart <= dataend) + datastart++; + while (isspace(*dataend) && datastart <= dataend) + dataend--; + + /* strip off quotes */ + if (datastart != dataend && *datastart == '"' && *dataend == '"') + datastart++, dataend--; + + /* check against range */ + switch (data->range_type) + { + case OPTION_RANGE_NONE: + /* do nothing */ + break; + + case OPTION_RANGE_INT: + /* check against integer range */ + i = 0; + if (sscanf(datastart, "%d", &i) != 1) + { + message(opts, OPTMSG_ERROR, "Illegal integer value for %s; keeping value of %s\n", astring_c(data->links[0].name), astring_c(data->data)); + data->error_reported = TRUE; + return; + } + if (i < data->range_minimum.i || i > data->range_maximum.i) + { + message(opts, OPTMSG_ERROR, "Invalid %s value (must be between %i and %i); keeping value of %s\n", + astring_c(data->links[0].name), data->range_minimum.i, data->range_maximum.i, astring_c(data->data)); + data->error_reported = TRUE; + return; + } + break; + + case OPTION_RANGE_FLOAT: + /* check against float range */ + f = 0; + if (sscanf(datastart, "%f", &f) != 1) + { + message(opts, OPTMSG_ERROR, "Illegal float value for %s; keeping value of %s\n", astring_c(data->links[0].name), astring_c(data->data)); + data->error_reported = TRUE; + return; + } + if (f < data->range_minimum.f || f > data->range_maximum.f) + { + message(opts, OPTMSG_ERROR, "Invalid %s value (must be between %f and %f); keeping value of %s\n", + astring_c(data->links[0].name), data->range_minimum.f, data->range_maximum.f, astring_c(data->data)); + data->error_reported = TRUE; + return; + } + break; + } + + /* ignore if we don't have priority */ + if (priority < data->priority) + return; + + /* allocate a copy of the data */ + astring_cpych(data->data, datastart, dataend + 1 - datastart); + data->priority = priority; + + /* bump the seqid and clear the error reporting */ + data->seqid++; + data->error_reported = FALSE; +} + + +/*------------------------------------------------- + parse_option_name - read data from an option_entry + name into an option_data structure +-------------------------------------------------*/ + +static int parse_option_name(core_options *opts, const char *srcstring, options_data *data) +{ + const char *start; + const char *end = NULL; + int curentry; + + /* start with the original string and loop over entries */ + start = srcstring; + for (curentry = 0; curentry < ARRAY_LENGTH(data->links); curentry++) + { + /* find the end of this entry and copy the string */ + for (end = start; *end != 0 && *end != ';' && *end != '('; end++) + ; + data->links[curentry].name = astring_dupch(start, end - start); + + /* if we hit the end of the source, stop */ + if (*end != ';') + break; + start = end + 1; + } + + /* have we found a range? */ + if (end != NULL && *end == '(') + { + if (sscanf(end, "(%d-%d)", &data->range_minimum.i, &data->range_maximum.i) == 2) + data->range_type = OPTION_RANGE_INT; + else if (sscanf(end, "(%f-%f)", &data->range_minimum.f, &data->range_maximum.f) == 2) + data->range_type = OPTION_RANGE_FLOAT; + } + return curentry; +} + + +/*------------------------------------------------- + message - outputs a message to a listener +-------------------------------------------------*/ + +static void message(core_options *opts, options_message msgtype, const char *format, ...) +{ + char buf[1024]; + va_list argptr; + + /* output a message if there is a non-NULL handler for it */ + if (opts->output[msgtype] != NULL) + { + va_start(argptr, format); + vsprintf(buf, format, argptr); + va_end(argptr); + + (*opts->output[msgtype])(buf); + } +} + + +/*------------------------------------------------- + hash_value - computes the hash value for a string +-------------------------------------------------*/ + +static UINT32 hash_value(core_options *opts, const char *str) +{ + UINT32 hash = 5381; + int c; + + while ((c = *str++) != 0) + hash = ((hash << 5) + hash) + c; + + return hash % ARRAY_LENGTH(opts->hashtable); +} + + +/*------------------------------------------------- + output_printf - outputs an arbitrary message + to a callback +-------------------------------------------------*/ + +static void output_printf(void (*output)(const char *s), const char *format, ...) +{ + char buf[1024]; + va_list argptr; + + va_start(argptr, format); + vsprintf(buf, format, argptr); + va_end(argptr); + + output(buf); +} diff --git a/src/lib/util/options.h b/src/lib/util/options.h new file mode 100644 index 00000000000..35abc73f226 --- /dev/null +++ b/src/lib/util/options.h @@ -0,0 +1,216 @@ +/*************************************************************************** + + options.h + + Core options code code + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#ifndef __OPTIONS_H__ +#define __OPTIONS_H__ + +#include "osdcore.h" +#include "corefile.h" + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +/* unadorned option names */ +#define MAX_UNADORNED_OPTIONS 16 +#define OPTION_UNADORNED(x) (((x) < MAX_UNADORNED_OPTIONS) ? option_unadorned[x] : "") + +/* option flags */ +#define OPTION_BOOLEAN 0x0001 /* option is a boolean value */ +#define OPTION_DEPRECATED 0x0002 /* option is deprecated */ +#define OPTION_COMMAND 0x0004 /* option is a command */ +#define OPTION_HEADER 0x0008 /* text-only header */ +#define OPTION_INTERNAL 0x0010 /* option is internal-only */ +#define OPTION_REPEATS 0x0020 /* unadorned option repeats */ + +/* option priorities */ +#define OPTION_PRIORITY_DEFAULT 0 /* defaults are at 0 priority */ +#define OPTION_PRIORITY_LOW 50 /* low priority */ +#define OPTION_PRIORITY_NORMAL 100 /* normal priority */ +#define OPTION_PRIORITY_HIGH 150 /* high priority */ +#define OPTION_PRIORITY_MAXIMUM 255 /* maximum priority */ + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* opaque type representing a collection of options */ +typedef struct _core_options core_options; + +/* opaque type use for enumeration of options */ +typedef struct _options_enumerator options_enumerator; + +/* describes a single option with its description and default value */ +typedef struct _options_entry options_entry; +struct _options_entry +{ + const char * name; /* name on the command line */ + const char * defvalue; /* default value of this argument */ + UINT32 flags; /* flags to describe the option */ + const char * description; /* description for -showusage */ +}; + +/* output messages are one of the following types */ +enum _options_message +{ + OPTMSG_INFO, + OPTMSG_WARNING, + OPTMSG_ERROR, + OPTMSG_COUNT +}; +typedef enum _options_message options_message; + +/* option ranges are one of the following types */ +enum _options_range_type +{ + OPTION_RANGE_NONE, + OPTION_RANGE_INT, + OPTION_RANGE_FLOAT +}; +typedef enum _options_range_type options_range_type; + + + +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +extern const char *option_unadorned[MAX_UNADORNED_OPTIONS]; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + + +/* ----- options collection management ----- */ + +/* create a new collection of options */ +core_options *options_create(void (*fail)(const char *message)); + +/* free a collection of options */ +void options_free(core_options *opts); + +/* set a callback for a particular class of message */ +void options_set_output_callback(core_options *opts, options_message msgtype, void (*callback)(const char *s)); + +/* revert options at or below a certain priority back to their defaults */ +void options_revert(core_options *opts, int priority); + +/* copy one collection of options into another */ +int options_copy(core_options *dest_opts, core_options *src_opts); + +/* compare two collections of options */ +int options_equal(core_options *opts1, core_options *opts2); + + + +/* ----- option definitions ----- */ + +/* add a set of entries to an options collection */ +int options_add_entries(core_options *opts, const options_entry *entrylist); + +/* set the default value for a particular option entry */ +int options_set_option_default_value(core_options *opts, const char *name, const char *defvalue); + +/* set a callback for a particular option entry */ +int options_set_option_callback(core_options *opts, const char *name, void (*callback)(core_options *opts, const char *arg)); + + + +/* ----- option data extraction ----- */ + +/* parse option data from a command line */ +int options_parse_command_line(core_options *opts, int argc, char **argv, int priority); + +/* parse option data from an INI file */ +int options_parse_ini_file(core_options *opts, core_file *inifile, int priority); + + + +/* ----- options output ----- */ + +/* output option data to an INI file */ +void options_output_ini_file(core_options *opts, core_file *inifile); + +/* output differing option data to an INI file */ +void options_output_diff_ini_file(core_options *opts, core_options *baseopts, core_file *inifile); + +/* output option data to a standard file handle */ +void options_output_ini_stdfile(core_options *opts, FILE *inifile); + +/* output help using the specified output function */ +void options_output_help(core_options *opts, void (*output)(const char *s)); + + + +/* ----- options reading ----- */ + +/* read an option as a string */ +const char *options_get_string(core_options *opts, const char *name); + +/* read an option as a boolean */ +int options_get_bool(core_options *opts, const char *name); + +/* read an option as an integer */ +int options_get_int(core_options *opts, const char *name); + +/* read an option as a floating point value */ +float options_get_float(core_options *opts, const char *name); + +/* read an option as a string */ +UINT32 options_get_seqid(core_options *opts, const char *name); + + + +/* ----- options setting ----- */ + +/* set an option as a string */ +void options_set_string(core_options *opts, const char *name, const char *value, int priority); + +/* set an option as a boolean */ +void options_set_bool(core_options *opts, const char *name, int value, int priority); + +/* set an option as an integer */ +void options_set_int(core_options *opts, const char *name, int value, int priority); + +/* set an option as a floating point value */ +void options_set_float(core_options *opts, const char *name, float value, int priority); + + + +/* ----- option definition queries ----- */ + +/* begin enumerating option definitions */ +options_enumerator *options_enumerator_begin(core_options *opts); + +/* get the next option in sequence */ +const char *options_enumerator_next(options_enumerator *enumerator); + +/* free memory allocated for enumeration */ +void options_enumerator_free(options_enumerator *enumerator); + +/* get the type of range for a given option */ +options_range_type options_get_range_type(core_options *opts, const char *name); + +/* return an integer range for a given option */ +void options_get_range_int(core_options *opts, const char *name, int *minval, int *maxval); + +/* return a floating point range for a given option */ +void options_get_range_float(core_options *opts, const char *name, float *minval, float *maxval); + + +#endif /* __OPTIONS_H__ */ diff --git a/src/lib/util/palette.c b/src/lib/util/palette.c new file mode 100644 index 00000000000..83b1a851b02 --- /dev/null +++ b/src/lib/util/palette.c @@ -0,0 +1,651 @@ +/****************************************************************************** + + palette.c + + Palette handling functions. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +******************************************************************************/ + +#include "palette.h" + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* object to track dirty states */ +typedef struct _dirty_state dirty_state; +struct _dirty_state +{ + UINT32 * dirty; /* bitmap of dirty entries */ + UINT32 mindirty; /* minimum dirty entry */ + UINT32 maxdirty; /* minimum dirty entry */ +}; + + +/* a single palette client */ +struct _palette_client +{ + palette_client *next; /* pointer to next client */ + palette_t * palette; /* reference to the palette */ + dirty_state live; /* live dirty state */ + dirty_state previous; /* previous dirty state */ +}; + + +/* a palette object */ +struct _palette_t +{ + UINT32 refcount; /* reference count on the palette */ + UINT32 numcolors; /* number of colors in the palette */ + UINT32 numgroups; /* number of groups in the palette */ + + rgb_t * entry_color; /* array of raw colors */ + float * entry_contrast; /* contrast value for each entry */ + rgb_t * adjusted_color; /* array of adjusted colors */ + rgb_t * adjusted_rgb15; /* array of adjusted colors as RGB15 */ + + float * group_bright; /* brightness value for each group */ + float * group_contrast; /* contrast value for each group */ + + palette_client *client_list; /* list of clients for this palette */ +}; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +static void internal_palette_free(palette_t *palette); +static void update_adjusted_color(palette_t *palette, UINT32 group, UINT32 index); + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + adjust_palette_entry - adjust a palette + entry for brightness +-------------------------------------------------*/ + +INLINE rgb_t adjust_palette_entry(rgb_t entry, float brightness, float contrast) +{ + int r = rgb_clamp((float)RGB_RED(entry) * contrast + brightness); + int g = rgb_clamp((float)RGB_GREEN(entry) * contrast + brightness); + int b = rgb_clamp((float)RGB_BLUE(entry) * contrast + brightness); + return MAKE_RGB(r,g,b); +} + + + +/*************************************************************************** + PALETTE ALLOCATION +***************************************************************************/ + +/*------------------------------------------------- + palette_alloc - allocate a new palette object + and take a single reference on it +-------------------------------------------------*/ + +palette_t *palette_alloc(UINT32 numcolors, UINT32 numgroups) +{ + palette_t *palette; + UINT32 index; + + /* allocate memory */ + palette = malloc(sizeof(*palette)); + if (palette == NULL) + goto error; + memset(palette, 0, sizeof(*palette)); + + /* allocate an array of palette entries and individual contrasts for each */ + palette->entry_color = malloc(sizeof(*palette->entry_color) * numcolors); + palette->entry_contrast = malloc(sizeof(*palette->entry_contrast) * numcolors); + if (palette->entry_color == NULL || palette->entry_contrast == NULL) + goto error; + + /* initialize the entries */ + for (index = 0; index < numcolors; index++) + { + palette->entry_color[index] = RGB_BLACK; + palette->entry_contrast[index] = 1.0f; + } + + /* allocate an array of brightness and contrast for each group */ + palette->group_bright = malloc(sizeof(*palette->group_bright) * numgroups); + palette->group_contrast = malloc(sizeof(*palette->group_contrast) * numgroups); + if (palette->group_bright == NULL || palette->group_contrast == NULL) + goto error; + + /* initialize the entries */ + for (index = 0; index < numgroups; index++) + { + palette->group_bright[index] = 0.0f; + palette->group_contrast[index] = 1.0f; + } + + /* allocate arrays for the adjusted colors */ + palette->adjusted_color = malloc(sizeof(*palette->adjusted_color) * (numcolors * numgroups + 2)); + palette->adjusted_rgb15 = malloc(sizeof(*palette->adjusted_rgb15) * (numcolors * numgroups + 2)); + if (palette->adjusted_color == NULL || palette->adjusted_rgb15 == NULL) + goto error; + + /* initialize the arrays */ + for (index = 0; index < numcolors * numgroups; index++) + { + palette->adjusted_color[index] = RGB_BLACK; + palette->adjusted_rgb15[index] = rgb_to_rgb15(RGB_BLACK); + } + + /* add black and white as the last two colors */ + palette->adjusted_color[index] = RGB_BLACK; + palette->adjusted_rgb15[index++] = rgb_to_rgb15(RGB_BLACK); + palette->adjusted_color[index] = RGB_WHITE; + palette->adjusted_rgb15[index++] = rgb_to_rgb15(RGB_WHITE); + + /* initialize the remainder of the structure */ + palette->refcount = 1; + palette->numcolors = numcolors; + palette->numgroups = numgroups; + return palette; + +error: + if (palette != NULL) + internal_palette_free(palette); + + return NULL; +} + + +/*------------------------------------------------- + palette_ref - reference a palette object, + incrementing its reference count +-------------------------------------------------*/ + +void palette_ref(palette_t *palette) +{ + palette->refcount++; +} + + +/*------------------------------------------------- + palette_deref - dereference a palette object; + if the reference count goes to 0, it is freed +-------------------------------------------------*/ + +void palette_deref(palette_t *palette) +{ + /* if the reference count goes to 0, free */ + if (--palette->refcount == 0) + internal_palette_free(palette); +} + + + +/*************************************************************************** + PALETTE INFORMATION +***************************************************************************/ + +/*------------------------------------------------- + palette_get_num_colors - return the number of + colors allocated in the palette +-------------------------------------------------*/ + +int palette_get_num_colors(palette_t *palette) +{ + return palette->numcolors; +} + + +/*------------------------------------------------- + palette_get_num_groups - return the number of + groups managed by the palette +-------------------------------------------------*/ + +int palette_get_num_groups(palette_t *palette) +{ + return palette->numgroups; +} + + +/*------------------------------------------------- + palette_get_black_entry - return the index of + the black entry +-------------------------------------------------*/ + +UINT32 palette_get_black_entry(palette_t *palette) +{ + return palette->numcolors * palette->numgroups + 0; +} + + +/*------------------------------------------------- + palette_get_white_entry - return the index of + the white entry +-------------------------------------------------*/ + +UINT32 palette_get_white_entry(palette_t *palette) +{ + return palette->numcolors * palette->numgroups + 1; +} + + + +/*************************************************************************** + PALETTE CLIENTS +***************************************************************************/ + +/*------------------------------------------------- + palette_client_alloc - add a new client to a + palette +-------------------------------------------------*/ + +palette_client *palette_client_alloc(palette_t *palette) +{ + UINT32 total_colors = palette->numcolors * palette->numgroups; + UINT32 dirty_dwords = (total_colors + 31) / 32; + palette_client *client; + + /* allocate memory for the client */ + client = malloc(sizeof(*client)); + if (client == NULL) + goto error; + memset(client, 0, sizeof(*client)); + + /* allocate dirty lists */ + client->live.dirty = malloc(dirty_dwords * sizeof(UINT32)); + client->previous.dirty = malloc(dirty_dwords * sizeof(UINT32)); + if (client->live.dirty == NULL || client->previous.dirty == NULL) + goto error; + + /* mark everything dirty to start with */ + memset(client->live.dirty, 0xff, dirty_dwords * sizeof(UINT32)); + memset(client->previous.dirty, 0xff, dirty_dwords * sizeof(UINT32)); + client->live.dirty[dirty_dwords - 1] &= (1 << (total_colors % 32)) - 1; + client->previous.dirty[dirty_dwords - 1] &= (1 << (total_colors % 32)) - 1; + + /* initialize the rest of the structure and add a reference to a palette */ + client->palette = palette; + palette_ref(palette); + client->live.mindirty = 0; + client->live.maxdirty = total_colors - 1; + + /* now add us to the list of clients */ + client->next = palette->client_list; + palette->client_list = client; + return client; + +error: + if (client != NULL) + { + if (client->live.dirty != NULL) + free(client->live.dirty); + if (client->previous.dirty != NULL) + free(client->previous.dirty); + free(client); + } + return NULL; +} + + +/*------------------------------------------------- + palette_client_free - remove a client from a + palette +-------------------------------------------------*/ + +void palette_client_free(palette_client *client) +{ + palette_client **curptr; + + /* first locate and remove ourself from the palette's list */ + for (curptr = &client->palette->client_list; *curptr != NULL; curptr = &(*curptr)->next) + if (*curptr == client) + { + *curptr = client->next; + break; + } + + /* now deref the palette */ + palette_deref(client->palette); + + /* free our data */ + if (client->live.dirty != NULL) + free(client->live.dirty); + if (client->previous.dirty != NULL) + free(client->previous.dirty); + free(client); +} + + +/*------------------------------------------------- + palette_client_get_palette - return a pointer + to the palette for this client +-------------------------------------------------*/ + +palette_t *palette_client_get_palette(palette_client *client) +{ + return client->palette; +} + + +/*------------------------------------------------- + palette_client_get_dirty_list - atomically get + the current dirty list for a client +-------------------------------------------------*/ + +const UINT32 *palette_client_get_dirty_list(palette_client *client, UINT32 *mindirty, UINT32 *maxdirty) +{ + dirty_state temp; + + /* fill in the mindirty/maxdirty */ + if (mindirty != NULL) + *mindirty = client->live.mindirty; + if (maxdirty != NULL) + *maxdirty = client->live.maxdirty; + + /* if nothing to report, report nothing and don't swap */ + if (client->live.mindirty > client->live.maxdirty) + return NULL; + + /* swap the live and previous lists */ + temp = client->live; + client->live = client->previous; + client->previous = temp; + + /* erase relevant entries in the new live one */ + if (client->live.mindirty <= client->live.maxdirty) + memset(client->live.dirty, client->live.mindirty / 8, (client->live.maxdirty / 8) + 1 - (client->live.mindirty / 8)); + client->live.mindirty = client->palette->numcolors * client->palette->numgroups; + client->live.maxdirty = 0; + + /* return a pointer to the previous table */ + return client->previous.dirty; +} + + + +/*************************************************************************** + PALETTE COLOR MANAGEMENT +***************************************************************************/ + +/*------------------------------------------------- + palette_entry_set_color - set the raw RGB + color for a given palette index +-------------------------------------------------*/ + +void palette_entry_set_color(palette_t *palette, UINT32 index, rgb_t rgb) +{ + int groupnum; + + /* if out of range, or unchanged, ignore */ + if (index >= palette->numcolors || palette->entry_color[index] == rgb) + return; + + /* set the color */ + palette->entry_color[index] = rgb; + + /* update across all groups */ + for (groupnum = 0; groupnum < palette->numgroups; groupnum++) + update_adjusted_color(palette, groupnum, index); +} + + +/*------------------------------------------------- + palette_entry_get_color - return the raw RGB + color for a given palette index +-------------------------------------------------*/ + +rgb_t palette_entry_get_color(palette_t *palette, UINT32 index) +{ + return (index < palette->numcolors) ? palette->entry_color[index] : RGB_BLACK; +} + + +/*------------------------------------------------- + palette_entry_get_adjusted_color - return the + adjusted RGB color (after all adjustments) for + a given palette index +-------------------------------------------------*/ + +rgb_t palette_entry_get_adjusted_color(palette_t *palette, UINT32 index) +{ + return (index < palette->numcolors * palette->numgroups) ? palette->adjusted_color[index] : RGB_BLACK; +} + + +/*------------------------------------------------- + palette_entry_list_raw - return the entire + palette as an array of raw RGB values +-------------------------------------------------*/ + +const rgb_t *palette_entry_list_raw(palette_t *palette) +{ + return palette->entry_color; +} + + +/*------------------------------------------------- + palette_entry_list_adjusted - return the + entire palette as an array of adjusted RGB + values +-------------------------------------------------*/ + +const rgb_t *palette_entry_list_adjusted(palette_t *palette) +{ + return palette->adjusted_color; +} + + +/*------------------------------------------------- + palette_entry_list_adjusted_rgb15 - return the + entire palette as an array of adjusted RGB-15 + values +-------------------------------------------------*/ + +const rgb_t *palette_entry_list_adjusted_rgb15(palette_t *palette) +{ + return palette->adjusted_rgb15; +} + + + +/*************************************************************************** + PALETTE ADJUSTMENTS +***************************************************************************/ + +/*------------------------------------------------- + palette_entry_set_contrast - set the contrast + adjustment for a single palette index +-------------------------------------------------*/ + +void palette_entry_set_contrast(palette_t *palette, UINT32 index, float contrast) +{ + int groupnum; + + /* if out of range, or unchanged, ignore */ + if (index >= palette->numcolors || palette->entry_contrast[index] == contrast) + return; + + /* set the contrast */ + palette->entry_contrast[index] = contrast; + + /* update across all groups */ + for (groupnum = 0; groupnum < palette->numgroups; groupnum++) + update_adjusted_color(palette, groupnum, index); +} + + +/*------------------------------------------------- + palette_entry_get_contrast - return the + contrast adjustment for a single palette index +-------------------------------------------------*/ + +float palette_entry_get_contrast(palette_t *palette, UINT32 index) +{ + return (index < palette->numcolors) ? palette->entry_contrast[index] : 1.0f; +} + + +/*------------------------------------------------- + palette_group_set_brightness - configure + overall brightness for a palette group +-------------------------------------------------*/ + +void palette_group_set_brightness(palette_t *palette, UINT32 group, float brightness) +{ + int index; + + /* if out of range, or unchanged, ignore */ + if (group >= palette->numgroups || palette->group_bright[group] == brightness) + return; + + /* set the contrast */ + palette->group_bright[group] = brightness; + + /* update across all colors */ + for (index = 0; index < palette->numcolors; index++) + update_adjusted_color(palette, group, index); +} + + +/*------------------------------------------------- + palette_group_set_contrast - configure + overall contrast for a palette group +-------------------------------------------------*/ + +void palette_group_set_contrast(palette_t *palette, UINT32 group, float contrast) +{ + int index; + + /* if out of range, or unchanged, ignore */ + if (group >= palette->numgroups || palette->group_contrast[group] == contrast) + return; + + /* set the contrast */ + palette->group_contrast[group] = contrast; + + /* update across all colors */ + for (index = 0; index < palette->numcolors; index++) + update_adjusted_color(palette, group, index); +} + + + +/*************************************************************************** + PALETTE UTILITIES +***************************************************************************/ + +/*------------------------------------------------- + palette_normalize_range - normalize a range + of palette entries +-------------------------------------------------*/ + +void palette_normalize_range(palette_t *palette, UINT32 start, UINT32 end, int lum_min, int lum_max) +{ + UINT32 ymin = 1000 * 255, ymax = 0; + UINT32 tmin, tmax; + UINT32 index; + + /* clamp within range */ + start = MAX(start, 0); + end = MIN(end, palette->numcolors - 1); + + /* find the minimum and maximum brightness of all the colors in the range */ + for (index = start; index <= end; index++) + { + rgb_t rgb = palette->entry_color[index]; + UINT32 y = 299 * RGB_RED(rgb) + 587 * RGB_GREEN(rgb) + 114 * RGB_BLUE(rgb); + ymin = MIN(ymin, y); + ymax = MAX(ymax, y); + } + + /* determine target minimum/maximum */ + tmin = (lum_min < 0) ? ((ymin + 500) / 1000) : lum_min; + tmax = (lum_max < 0) ? ((ymax + 500) / 1000) : lum_max; + + /* now normalize the palette */ + for (index = start; index <= end; index++) + { + rgb_t rgb = palette->entry_color[index]; + UINT32 y = 299 * RGB_RED(rgb) + 587 * RGB_GREEN(rgb) + 114 * RGB_BLUE(rgb); + UINT32 target = tmin + ((y - ymin) * (tmax - tmin + 1)) / (ymax - ymin); + UINT8 r = (y == 0) ? 0 : rgb_clamp(RGB_RED(rgb) * 1000 * target / y); + UINT8 g = (y == 0) ? 0 : rgb_clamp(RGB_GREEN(rgb) * 1000 * target / y); + UINT8 b = (y == 0) ? 0 : rgb_clamp(RGB_BLUE(rgb) * 1000 * target / y); + palette_entry_set_color(palette, index, MAKE_RGB(r, g, b)); + } +} + + + +/*************************************************************************** + INTERNAL ROUTINES +***************************************************************************/ + +/*------------------------------------------------- + internal_palette_free - free all allocations + from a palette +-------------------------------------------------*/ + +static void internal_palette_free(palette_t *palette) +{ + /* free per-color data */ + if (palette->entry_color != NULL) + free(palette->entry_color); + if (palette->entry_contrast != NULL) + free(palette->entry_contrast); + + /* free per-group data */ + if (palette->group_bright != NULL) + free(palette->group_bright); + if (palette->group_contrast != NULL) + free(palette->group_contrast); + + /* free adjusted data */ + if (palette->adjusted_color != NULL) + free(palette->adjusted_color); + if (palette->adjusted_rgb15 != NULL) + free(palette->adjusted_rgb15); + + /* and the palette itself */ + free(palette); +} + + +/*------------------------------------------------- + update_adjusted_color - update a color index + by group and index pair +-------------------------------------------------*/ + +static void update_adjusted_color(palette_t *palette, UINT32 group, UINT32 index) +{ + UINT32 finalindex = group * palette->numcolors + index; + palette_client *client; + rgb_t adjusted; + + /* compute the adjusted value */ + adjusted = adjust_palette_entry(palette->entry_color[index], palette->group_bright[group], palette->group_contrast[group] * palette->entry_contrast[index]); + + /* if not different, ignore */ + if (palette->adjusted_color[finalindex] == adjusted) + return; + + /* otherwise, modify the adjusted color array */ + palette->adjusted_color[finalindex] = adjusted; + palette->adjusted_rgb15[finalindex] = rgb_to_rgb15(adjusted); + + /* mark dirty in all clients */ + for (client = palette->client_list; client != NULL; client = client->next) + { + client->live.dirty[finalindex / 32] |= 1 << (finalindex % 32); + client->live.mindirty = MIN(client->live.mindirty, finalindex); + client->live.maxdirty = MAX(client->live.maxdirty, finalindex); + } +} diff --git a/src/lib/util/palette.h b/src/lib/util/palette.h new file mode 100644 index 00000000000..cef180c1262 --- /dev/null +++ b/src/lib/util/palette.h @@ -0,0 +1,259 @@ +/****************************************************************************** + + palette.c + + Core palette routines. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#pragma once + +#ifndef __PALETTE_H__ +#define __PALETTE_H__ + +#include "osdcore.h" + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* an rgb_t is a single combined R,G,B (and optionally alpha) value */ +typedef UINT32 rgb_t; + +/* an rgb15_t is a single combined 15-bit R,G,B value */ +typedef UINT16 rgb15_t; + +/* a palette is an opaque, reference counted object */ +typedef struct _palette_t palette_t; + +/* a palette client is someone who is tracking the dirty state of a palette */ +typedef struct _palette_client palette_client; + + + +/*************************************************************************** + MACROS +***************************************************************************/ + +/* macros to assemble rgb_t values */ +#define MAKE_RGB(r,g,b) ((((rgb_t)(r) & 0xff) << 16) | (((rgb_t)(g) & 0xff) << 8) | ((rgb_t)(b) & 0xff)) +#define MAKE_ARGB(a,r,g,b) (MAKE_RGB(r,g,b) | (((rgb_t)(a) & 0xff) << 24)) + +/* macros to extract components from rgb_t values */ +#define RGB_ALPHA(rgb) (((rgb) >> 24) & 0xff) +#define RGB_RED(rgb) (((rgb) >> 16) & 0xff) +#define RGB_GREEN(rgb) (((rgb) >> 8) & 0xff) +#define RGB_BLUE(rgb) ((rgb) & 0xff) + +/* common colors */ +#define RGB_BLACK (MAKE_RGB(0,0,0)) +#define RGB_WHITE (MAKE_RGB(255,255,255)) + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + + +/* ----- palette allocation ----- */ + +/* allocate a new palette object and take a single reference on it */ +palette_t *palette_alloc(UINT32 numcolors, UINT32 numgroups); + +/* reference a palette object, incrementing its reference count */ +void palette_ref(palette_t *palette); + +/* dereference a palette object; if the reference count goes to 0, it is freed */ +void palette_deref(palette_t *palette); + + + +/* ----- palette information ----- */ + +/* return the number of colors managed by the palette */ +int palette_get_num_colors(palette_t *palette); + +/* return the number of groups managed by the palette */ +int palette_get_num_groups(palette_t *palette); + +/* return the index of the black entry */ +UINT32 palette_get_black_entry(palette_t *palette); + +/* return the index of the white entry */ +UINT32 palette_get_white_entry(palette_t *palette); + + + +/* ----- palette clients ----- */ + +/* add a new client to a palette */ +palette_client *palette_client_alloc(palette_t *palette); + +/* remove a client from a palette */ +void palette_client_free(palette_client *client); + +/* return a pointer to the palette for this client */ +palette_t *palette_client_get_palette(palette_client *client); + +/* atomically get the current dirty list for a client */ +const UINT32 *palette_client_get_dirty_list(palette_client *client, UINT32 *mindirty, UINT32 *maxdirty); + + + +/* ----- color management ----- */ + +/* set the raw RGB color for a given palette index */ +void palette_entry_set_color(palette_t *palette, UINT32 index, rgb_t rgb); + +/* return the raw RGB color for a given palette index */ +rgb_t palette_entry_get_color(palette_t *palette, UINT32 index); + +/* return the adjusted RGB color (after all adjustments) for a given palette index */ +rgb_t palette_entry_get_adjusted_color(palette_t *palette, UINT32 index); + +/* return the entire palette as an array of raw RGB values */ +const rgb_t *palette_entry_list_raw(palette_t *palette); + +/* return the entire palette as an array of adjusted RGB values */ +const rgb_t *palette_entry_list_adjusted(palette_t *palette); + +/* return the entire palette as an array of adjusted RGB-15 values */ +const rgb_t *palette_entry_list_adjusted_rgb15(palette_t *palette); + + + +/* ----- palette adjustments ----- */ + +/* set the contrast adjustment for a single palette index */ +void palette_entry_set_contrast(palette_t *palette, UINT32 index, float contrast); + +/* return the contrast adjustment for a single palette index */ +float palette_entry_get_contrast(palette_t *palette, UINT32 index); + +/* configure overall brightness for a palette group */ +void palette_group_set_brightness(palette_t *palette, UINT32 group, float brightness); + +/* configure overall contrast for a palette group */ +void palette_group_set_contrast(palette_t *palette, UINT32 group, float contrast); + + + +/* ----- palette utilities ----- */ + +/* normalize a range of palette entries, mapping minimum brightness to lum_min and maximum + brightness to lum_max; if either value is < 0, that boundary value is not modified */ +void palette_normalize_range(palette_t *palette, UINT32 start, UINT32 end, int lum_min, int lum_max); + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + rgb_to_rgb15 - convert an RGB triplet to + a 15-bit OSD-specified RGB value +-------------------------------------------------*/ + +INLINE rgb15_t rgb_to_rgb15(rgb_t rgb) +{ + return ((RGB_RED(rgb) >> 3) << 10) | ((RGB_GREEN(rgb) >> 3) << 5) | ((RGB_BLUE(rgb) >> 3) << 0); +} + + +/*------------------------------------------------- + rgb_clamp - clamp an RGB component to 0-255 +-------------------------------------------------*/ + +INLINE UINT8 rgb_clamp(INT32 value) +{ + if (value < 0) + return 0; + if (value > 255) + return 255; + return value; +} + + +/*------------------------------------------------- + pal1bit - convert a 1-bit value to 8 bits +-------------------------------------------------*/ + +INLINE UINT8 pal1bit(UINT8 bits) +{ + return (bits & 1) ? 0xff : 0x00; +} + + +/*------------------------------------------------- + pal2bit - convert a 2-bit value to 8 bits +-------------------------------------------------*/ + +INLINE UINT8 pal2bit(UINT8 bits) +{ + bits &= 3; + return (bits << 6) | (bits << 4) | (bits << 2) | bits; +} + + +/*------------------------------------------------- + pal3bit - convert a 3-bit value to 8 bits +-------------------------------------------------*/ + +INLINE UINT8 pal3bit(UINT8 bits) +{ + bits &= 7; + return (bits << 5) | (bits << 2) | (bits >> 1); +} + + +/*------------------------------------------------- + pal4bit - convert a 4-bit value to 8 bits +-------------------------------------------------*/ + +INLINE UINT8 pal4bit(UINT8 bits) +{ + bits &= 0xf; + return (bits << 4) | bits; +} + + +/*------------------------------------------------- + pal5bit - convert a 5-bit value to 8 bits +-------------------------------------------------*/ + +INLINE UINT8 pal5bit(UINT8 bits) +{ + bits &= 0x1f; + return (bits << 3) | (bits >> 2); +} + + +/*------------------------------------------------- + pal6bit - convert a 6-bit value to 8 bits +-------------------------------------------------*/ + +INLINE UINT8 pal6bit(UINT8 bits) +{ + bits &= 0x3f; + return (bits << 2) | (bits >> 4); +} + + +/*------------------------------------------------- + pal7bit - convert a 7-bit value to 8 bits +-------------------------------------------------*/ + +INLINE UINT8 pal7bit(UINT8 bits) +{ + bits &= 0x7f; + return (bits << 1) | (bits >> 6); +} + + +#endif /* __PALETTE_H__ */ diff --git a/src/lib/util/png.c b/src/lib/util/png.c new file mode 100644 index 00000000000..865703c0d73 --- /dev/null +++ b/src/lib/util/png.c @@ -0,0 +1,1143 @@ +/********************************************************************* + + png.c + + PNG reading functions. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + + +*********************************************************************/ + +#include <math.h> +#include <zlib.h> +#include "png.h" + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +typedef struct _image_data_chunk image_data_chunk; +struct _image_data_chunk +{ + image_data_chunk * next; + int length; + UINT8 * data; +}; + + +typedef struct _png_private png_private; +struct _png_private +{ + png_info * pnginfo; + image_data_chunk * idata; + image_data_chunk ** idata_next; + UINT8 bpp; + UINT32 rowbytes; +}; + + + +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +static const int samples[] = { 1, 0, 3, 1, 2, 0, 4 }; + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +INLINE UINT8 fetch_8bit(UINT8 *v) +{ + return *v; +} + + +INLINE UINT16 fetch_16bit(UINT8 *v) +{ + return BIG_ENDIANIZE_INT16(*(UINT16 *)v); +} + + +INLINE UINT32 fetch_32bit(UINT8 *v) +{ + return BIG_ENDIANIZE_INT32(*(UINT32 *)v); +} + + +INLINE void put_8bit(UINT8 *v, UINT8 data) +{ + *v = data; +} + + +INLINE void put_16bit(UINT8 *v, UINT16 data) +{ + *(UINT16 *)v = BIG_ENDIANIZE_INT16(data); +} + + +INLINE void put_32bit(UINT8 *v, UINT32 data) +{ + *(UINT32 *)v = BIG_ENDIANIZE_INT32(data); +} + + +INLINE int compute_bpp(const png_info *pnginfo) +{ + return samples[pnginfo->color_type] * pnginfo->bit_depth / 8; +} + + +INLINE int compute_rowbytes(const png_info *pnginfo) +{ + return (pnginfo->width * samples[pnginfo->color_type] * pnginfo->bit_depth + 7) / 8; +} + + + +/*************************************************************************** + GENERAL FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + png_free - free all memory allocated in a + pnginfo structure +-------------------------------------------------*/ + +void png_free(png_info *pnginfo) +{ + while (pnginfo->textlist != NULL) + { + png_text *temp = pnginfo->textlist; + pnginfo->textlist = temp->next; + if (temp->keyword != NULL) + free((void *)temp->keyword); + free(temp); + } + + if (pnginfo->palette != NULL) + free(pnginfo->palette); + pnginfo->palette = NULL; + + if (pnginfo->trans != NULL) + free(pnginfo->trans); + pnginfo->trans = NULL; + + if (pnginfo->image != NULL) + free(pnginfo->image); + pnginfo->image = NULL; +} + + + +/*************************************************************************** + PNG READING FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + verify_header - verify the PNG + header at the current file location +-------------------------------------------------*/ + +static png_error verify_header(core_file *fp) +{ + UINT8 signature[8]; + + /* read 8 bytes */ + if (core_fread(fp, signature, 8) != 8) + return PNGERR_FILE_TRUNCATED; + + /* return an error if we don't match */ + if (memcmp(signature, PNG_Signature, 8) != 0) + return PNGERR_BAD_SIGNATURE; + + return PNGERR_NONE; +} + + +/*------------------------------------------------- + read_chunk - read the next PNG chunk +-------------------------------------------------*/ + +static png_error read_chunk(core_file *fp, UINT8 **data, UINT32 *type, UINT32 *length) +{ + UINT32 crc, chunk_crc; + UINT8 tempbuff[4]; + + /* fetch the length of this chunk */ + if (core_fread(fp, tempbuff, 4) != 4) + return PNGERR_FILE_TRUNCATED; + *length = fetch_32bit(tempbuff); + + /* fetch the type of this chunk */ + if (core_fread(fp, tempbuff, 4) != 4) + return PNGERR_FILE_TRUNCATED; + *type = fetch_32bit(tempbuff); + + /* stop when we hit an IEND chunk */ + if (*type == PNG_CN_IEND) + return PNGERR_NONE; + + /* start the CRC with the chunk type (but not the length) */ + crc = crc32(0, tempbuff, 4); + + /* read the chunk itself into an allocated memory buffer */ + *data = NULL; + if (*length != 0) + { + /* allocate memory for this chunk */ + *data = (UINT8 *)malloc(*length); + if (*data == NULL) + return PNGERR_OUT_OF_MEMORY; + + /* read the data from the file */ + if (core_fread(fp, *data, *length) != *length) + { + free(*data); + *data = NULL; + return PNGERR_FILE_TRUNCATED; + } + + /* update the CRC */ + crc = crc32(crc, *data, *length); + } + + /* read the CRC */ + if (core_fread(fp, tempbuff, 4) != 4) + { + free(*data); + *data = NULL; + return PNGERR_FILE_TRUNCATED; + } + chunk_crc = fetch_32bit(tempbuff); + + /* validate the CRC */ + if (crc != chunk_crc) + { + free(*data); + *data = NULL; + return PNGERR_FILE_CORRUPT; + } + return PNGERR_NONE; +} + + +/*------------------------------------------------- + process_chunk - process a PNG chunk +-------------------------------------------------*/ + +static png_error process_chunk(png_private *png, UINT8 *data, UINT32 type, UINT32 length, int *keepmem) +{ + /* default to not keeping memory */ + *keepmem = FALSE; + + /* switch off of the type */ + switch (type) + { + /* image header */ + case PNG_CN_IHDR: + png->pnginfo->width = fetch_32bit(data); + png->pnginfo->height = fetch_32bit(data + 4); + png->pnginfo->bit_depth = fetch_8bit(data + 8); + png->pnginfo->color_type = fetch_8bit(data + 9); + png->pnginfo->compression_method = fetch_8bit(data + 10); + png->pnginfo->filter_method = fetch_8bit(data + 11); + png->pnginfo->interlace_method = fetch_8bit(data + 12); + break; + + /* palette */ + case PNG_CN_PLTE: + png->pnginfo->num_palette = length / 3; + png->pnginfo->palette = data; + *keepmem = TRUE; + break; + + /* transparency information */ + case PNG_CN_tRNS: + png->pnginfo->num_trans = length; + png->pnginfo->trans = data; + *keepmem = TRUE; + break; + + /* image data */ + case PNG_CN_IDAT: + + /* allocate a new image data descriptor */ + *png->idata_next = malloc(sizeof(**png->idata_next)); + if (*png->idata_next == NULL) + return PNGERR_OUT_OF_MEMORY; + + /* add it to the tail of the list */ + (*png->idata_next)->next = NULL; + (*png->idata_next)->length = length; + (*png->idata_next)->data = data; + png->idata_next = &(*png->idata_next)->next; + *keepmem = TRUE; + break; + + /* gamma */ + case PNG_CN_gAMA: + png->pnginfo->source_gamma = fetch_32bit(data) / 100000.0; + break; + + /* physical information */ + case PNG_CN_pHYs: + png->pnginfo->xres = fetch_32bit(data); + png->pnginfo->yres = fetch_32bit(data + 4); + png->pnginfo->resolution_unit = fetch_8bit(data + 8); + break; + + /* text */ + case PNG_CN_tEXt: + { + png_text *text, *pt, *ct; + + /* allocate a new text item */ + text = malloc(sizeof(*text)); + if (text == NULL) + return PNGERR_OUT_OF_MEMORY; + + /* set the elements */ + text->keyword = (char *)data; + text->text = text->keyword + strlen(text->keyword) + 1; + text->next = NULL; + + /* add to the end of the list */ + for (pt = NULL, ct = png->pnginfo->textlist; ct != NULL; pt = ct, ct = ct->next) ; + if (pt == NULL) + png->pnginfo->textlist = text; + else + pt->next = text; + + *keepmem = TRUE; + break; + } + + /* anything else */ + default: + if ((type & 0x20000000) == 0) + return PNGERR_UNKNOWN_CHUNK; + break; + } + return PNGERR_NONE; +} + + +/*------------------------------------------------- + unfilter_row - unfilter a single row of pixels +-------------------------------------------------*/ + +static png_error unfilter_row(int type, UINT8 *src, UINT8 *dst, UINT8 *dstprev, int bpp, int rowbytes) +{ + int x; + + /* switch off of it */ + switch (type) + { + /* no filter, just copy */ + case PNG_PF_None: + for (x = 0; x < rowbytes; x++) + *dst++ = *src++; + break; + + /* SUB = previous pixel */ + case PNG_PF_Sub: + for (x = 0; x < bpp; x++) + *dst++ = *src++; + for (x = bpp; x < rowbytes; x++, dst++) + *dst = *src++ + dst[-bpp]; + break; + + /* UP = pixel above */ + case PNG_PF_Up: + if (dstprev == NULL) + return unfilter_row(PNG_PF_None, src, dst, dstprev, bpp, rowbytes); + for (x = 0; x < rowbytes; x++, dst++) + *dst = *src++ + *dstprev++; + break; + + /* AVERAGE = average of pixel above and previous pixel */ + case PNG_PF_Average: + if (dstprev == NULL) + { + for (x = 0; x < bpp; x++) + *dst++ = *src++; + for (x = bpp; x < rowbytes; x++, dst++) + *dst = *src++ + dst[-bpp] / 2; + } + else + { + for (x = 0; x < bpp; x++, dst++) + *dst = *src++ + *dstprev++ / 2; + for (x = bpp; x < rowbytes; x++, dst++) + *dst = *src++ + (*dstprev++ + dst[-bpp]) / 2; + } + break; + + /* PAETH = special filter */ + case PNG_PF_Paeth: + for (x = 0; x < rowbytes; x++) + { + INT32 pa = (x < bpp) ? 0 : dst[-bpp]; + INT32 pc = (x < bpp || dstprev == NULL) ? 0 : dstprev[-bpp]; + INT32 pb = (dstprev == NULL) ? 0 : *dstprev++; + INT32 prediction = pa + pb - pc; + INT32 da = abs(prediction - pa); + INT32 db = abs(prediction - pb); + INT32 dc = abs(prediction - pc); + if (da <= db && da <= dc) + *dst++ = *src++ + pa; + else if (db <= dc) + *dst++ = *src++ + pb; + else + *dst++ = *src++ + pc; + } + break; + + /* unknown filter type */ + default: + return PNGERR_UNKNOWN_FILTER; + } + + return PNGERR_NONE; +} + + +/*------------------------------------------------- + process_image - post-process a loaded iamge +-------------------------------------------------*/ + +static png_error process_image(png_private *png) +{ + int rowbytes, bpp, imagesize; + png_error error = PNGERR_NONE; + image_data_chunk *idat; + UINT8 *src, *dst; + z_stream stream; + int zerr, y; + + /* compute some basic parameters */ + bpp = compute_bpp(png->pnginfo); + rowbytes = compute_rowbytes(png->pnginfo); + imagesize = png->pnginfo->height * (rowbytes + 1); + + /* allocate memory for the filtered image */ + png->pnginfo->image = (UINT8 *)malloc(imagesize); + if (png->pnginfo->image == NULL) + return PNGERR_OUT_OF_MEMORY; + + /* initialize the stream */ + memset(&stream, 0, sizeof(stream)); + stream.next_out = png->pnginfo->image; + stream.avail_out = imagesize; + zerr = inflateInit(&stream); + if (zerr != Z_OK) + { + error = PNGERR_DECOMPRESS_ERROR; + goto handle_error; + } + + /* loop over IDAT and decompress each as part of a larger stream */ + for (idat = png->idata; idat != NULL; idat = idat->next) + { + /* decompress this chunk */ + stream.next_in = idat->data; + stream.avail_in = idat->length; + zerr = inflate(&stream, Z_NO_FLUSH); + + /* stop at the end of the stream */ + if (zerr == Z_STREAM_END) + break; + + /* other errors are fatal */ + if (zerr != Z_OK) + { + error = PNGERR_DECOMPRESS_ERROR; + goto handle_error; + } + } + + /* clean up */ + zerr = inflateEnd(&stream); + if (zerr != Z_OK) + { + error = PNGERR_DECOMPRESS_ERROR; + goto handle_error; + } + + /* we de-filter in place */ + src = dst = png->pnginfo->image; + + /* iterate over rows */ + for (y = 0; y < png->pnginfo->height && error == PNGERR_NONE; y++) + { + /* first byte of each row is the filter type */ + int filter = *src++; + error = unfilter_row(filter, src, dst, (y == 0) ? NULL : &dst[-rowbytes], bpp, rowbytes); + src += rowbytes; + dst += rowbytes; + } + +handle_error: + /* if we errored, free the image data */ + if (error != PNGERR_NONE) + { + free(png->pnginfo->image); + png->pnginfo->image = NULL; + } + return error; +} + + +/*------------------------------------------------- + png_read_file - read a PNG from a core stream +-------------------------------------------------*/ + +png_error png_read_file(core_file *fp, png_info *pnginfo) +{ + UINT8 *chunk_data = NULL; + png_private png; + png_error error; + + /* initialize the data structures */ + memset(&png, 0, sizeof(png)); + memset(pnginfo, 0, sizeof(*pnginfo)); + png.pnginfo = pnginfo; + png.idata_next = &png.idata; + + /* verify the signature at the start of the file */ + error = verify_header(fp); + if (error != PNGERR_NONE) + goto handle_error; + + /* loop until we hit an IEND chunk */ + for ( ; ; ) + { + UINT32 chunk_type, chunk_length; + int keepmem; + + /* read a chunk */ + error = read_chunk(fp, &chunk_data, &chunk_type, &chunk_length); + if (error != PNGERR_NONE) + goto handle_error; + + /* stop when we hit an IEND chunk */ + if (chunk_type == PNG_CN_IEND) + break; + + /* process the chunk */ + error = process_chunk(&png, chunk_data, chunk_type, chunk_length, &keepmem); + if (error != PNGERR_NONE) + goto handle_error; + + /* free memory if we didn't want to keep it */ + if (!keepmem) + free(chunk_data); + chunk_data = NULL; + } + + /* finish processing the image */ + error = process_image(&png); + if (error != PNGERR_NONE) + goto handle_error; + +handle_error: + + /* free all intermediate data */ + while (png.idata != NULL) + { + image_data_chunk *next = png.idata->next; + if (png.idata->data != NULL) + free(png.idata->data); + free(png.idata); + png.idata = next; + } + if (chunk_data != NULL) + free(chunk_data); + + /* if we have an error, free all the other data as well */ + if (error != PNGERR_NONE) + { + png_free(pnginfo); + memset(pnginfo, 0, sizeof(*pnginfo)); + } + return error; +} + + +/*------------------------------------------------- + png_read_bitmap - load a PNG file into a + bitmap_t +-------------------------------------------------*/ + +png_error png_read_bitmap(core_file *fp, bitmap_t **bitmap) +{ + png_error result; + png_info png; + UINT8 *src; + int x, y; + + /* read the PNG data */ + result = png_read_file(fp, &png); + if (result != PNGERR_NONE) + return result; + + /* verify we can handle this PNG */ + if (png.bit_depth > 8 || png.interlace_method != 0 || + (png.color_type != 0 && png.color_type != 3 && png.color_type != 2 && png.color_type != 6)) + { + png_free(&png); + return PNGERR_UNSUPPORTED_FORMAT; + } + + /* if less than 8 bits, upsample */ + png_expand_buffer_8bit(&png); + + /* allocate a bitmap of the appropriate size and copy it */ + *bitmap = bitmap_alloc(png.width, png.height, BITMAP_FORMAT_ARGB32); + if (*bitmap == NULL) + { + png_free(&png); + return PNGERR_OUT_OF_MEMORY; + } + + /* handle 8bpp palettized case */ + src = png.image; + if (png.color_type == 3) + { + /* loop over width/height */ + for (y = 0; y < png.height; y++) + for (x = 0; x < png.width; x++, src++) + { + /* determine alpha and expand to 32bpp */ + UINT8 alpha = (*src < png.num_trans) ? png.trans[*src] : 0xff; + *BITMAP_ADDR32(*bitmap, y, x) = (alpha << 24) | (png.palette[*src * 3] << 16) | (png.palette[*src * 3 + 1] << 8) | png.palette[*src * 3 + 2]; + } + } + + /* handle 8bpp grayscale case */ + else if (png.color_type == 0) + { + for (y = 0; y < png.height; y++) + for (x = 0; x < png.width; x++, src++) + *BITMAP_ADDR32(*bitmap, y, x) = 0xff000000 | (*src << 16) | (*src << 8) | *src; + } + + /* handle 32bpp non-alpha case */ + else if (png.color_type == 2) + { + for (y = 0; y < png.height; y++) + for (x = 0; x < png.width; x++, src += 3) + *BITMAP_ADDR32(*bitmap, y, x) = 0xff000000 | (src[0] << 16) | (src[1] << 8) | src[2]; + } + + /* handle 32bpp alpha case */ + else if (png.color_type == 6) + { + for (y = 0; y < png.height; y++) + for (x = 0; x < png.width; x++, src += 4) + *BITMAP_ADDR32(*bitmap, y, x) = (src[3] << 24) | (src[0] << 16) | (src[1] << 8) | src[2]; + } + + /* free our temporary data and return */ + png_free(&png); + return PNGERR_NONE; +} + + +/*------------------------------------------------- + png_expand_buffer_8bit - expand a buffer from + sub 8-bit to 8-bit +-------------------------------------------------*/ + +png_error png_expand_buffer_8bit(png_info *pnginfo) +{ + int i,j, k; + UINT8 *inp, *outp, *outbuf; + + /* nothing to do if we're at 8 or greater already */ + if (pnginfo->bit_depth >= 8) + return PNGERR_NONE; + + /* allocate a new buffer at 8-bit */ + outbuf = malloc(pnginfo->width * pnginfo->height); + if (outbuf == NULL) + return PNGERR_OUT_OF_MEMORY; + + inp = pnginfo->image; + outp = outbuf; + + for (i = 0; i < pnginfo->height; i++) + { + for(j = 0; j < pnginfo->width / ( 8 / pnginfo->bit_depth); j++) + { + for (k = 8 / pnginfo->bit_depth-1; k >= 0; k--) + *outp++ = (*inp >> k * pnginfo->bit_depth) & (0xff >> (8 - pnginfo->bit_depth)); + inp++; + } + if (pnginfo->width % (8 / pnginfo->bit_depth)) + { + for (k = pnginfo->width % (8 / pnginfo->bit_depth)-1; k >= 0; k--) + *outp++ = (*inp >> k * pnginfo->bit_depth) & (0xff >> (8 - pnginfo->bit_depth)); + inp++; + } + } + free (pnginfo->image); + pnginfo->image = outbuf; + + return PNGERR_NONE; +} + + + +/*************************************************************************** + PNG WRITING FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + png_add_text - add a text entry to the png_info +-------------------------------------------------*/ + +png_error png_add_text(png_info *pnginfo, const char *keyword, const char *text) +{ + png_text *newtext, *pt, *ct; + char *textdata; + int keylen; + + /* allocate a new text element */ + newtext = malloc(sizeof(*newtext)); + if (newtext == NULL) + return PNGERR_OUT_OF_MEMORY; + + /* allocate a string long enough to hold both */ + keylen = (int)strlen(keyword); + textdata = malloc(keylen + 1 + strlen(text) + 1); + if (textdata == NULL) + { + free(newtext); + return PNGERR_OUT_OF_MEMORY; + } + + /* copy in the data */ + strcpy(textdata, keyword); + strcpy(textdata + keylen + 1, text); + + /* text follows a trailing NULL */ + newtext->keyword = textdata; + newtext->text = textdata + keylen + 1; + newtext->next = NULL; + + /* add us to the end of the linked list */ + for (pt = NULL, ct = pnginfo->textlist; ct != NULL; pt = ct, ct = ct->next) ; + if (pt == NULL) + pnginfo->textlist = newtext; + else + pt->next = newtext; + + return PNGERR_NONE; +} + + +/*------------------------------------------------- + write_chunk - write an in-memory chunk to + the given file +-------------------------------------------------*/ + +static png_error write_chunk(core_file *fp, const UINT8 *data, UINT32 type, UINT32 length) +{ + UINT8 tempbuff[8]; + UINT32 crc; + + /* stuff the length/type into the buffer */ + put_32bit(tempbuff + 0, length); + put_32bit(tempbuff + 4, type); + crc = crc32(0, tempbuff + 4, 4); + + /* write that data */ + if (core_fwrite(fp, tempbuff, 8) != 8) + return PNGERR_FILE_ERROR; + + /* append the actual data */ + if (length > 0) + { + if (core_fwrite(fp, data, length) != length) + return PNGERR_FILE_ERROR; + crc = crc32(crc, data, length); + } + + /* write the CRC */ + put_32bit(tempbuff, crc); + if (core_fwrite(fp, tempbuff, 4) != 4) + return PNGERR_FILE_ERROR; + + return PNGERR_NONE; +} + + +/*------------------------------------------------- + write_deflated_chunk - write an in-memory + chunk to the given file by deflating it +-------------------------------------------------*/ + +static png_error write_deflated_chunk(core_file *fp, UINT8 *data, UINT32 type, UINT32 length) +{ + UINT64 lengthpos = core_ftell(fp); + UINT8 tempbuff[8192]; + UINT32 zlength = 0; + z_stream stream; + UINT32 crc; + int zerr; + + /* stuff the length/type into the buffer */ + put_32bit(tempbuff + 0, length); + put_32bit(tempbuff + 4, type); + crc = crc32(0, tempbuff + 4, 4); + + /* write that data */ + if (core_fwrite(fp, tempbuff, 8) != 8) + return PNGERR_FILE_ERROR; + + /* initialize the stream */ + memset(&stream, 0, sizeof(stream)); + stream.next_in = data; + stream.avail_in = length; + zerr = deflateInit(&stream, Z_DEFAULT_COMPRESSION); + if (zerr != Z_OK) + return PNGERR_COMPRESS_ERROR; + + /* now loop until we run out of data */ + for ( ; ; ) + { + /* compress this chunk */ + stream.next_out = tempbuff; + stream.avail_out = sizeof(tempbuff); + zerr = deflate(&stream, Z_FINISH); + + /* if there's data to write, do it */ + if (stream.avail_out < sizeof(tempbuff)) + { + int bytes = sizeof(tempbuff) - stream.avail_out; + if (core_fwrite(fp, tempbuff, bytes) != bytes) + { + deflateEnd(&stream); + return PNGERR_FILE_ERROR; + } + crc = crc32(crc, tempbuff, bytes); + zlength += bytes; + } + + /* stop at the end of the stream */ + if (zerr == Z_STREAM_END) + break; + + /* other errors are fatal */ + if (zerr != Z_OK) + { + deflateEnd(&stream); + return PNGERR_COMPRESS_ERROR; + } + } + + /* clean up deflater(maus) */ + zerr = deflateEnd(&stream); + if (zerr != Z_OK) + return PNGERR_COMPRESS_ERROR; + + /* write the CRC */ + put_32bit(tempbuff, crc); + if (core_fwrite(fp, tempbuff, 4) != 4) + return PNGERR_FILE_ERROR; + + /* seek back and update the length */ + core_fseek(fp, lengthpos, SEEK_SET); + put_32bit(tempbuff + 0, zlength); + if (core_fwrite(fp, tempbuff, 4) != 4) + return PNGERR_FILE_ERROR; + + /* return to the end */ + core_fseek(fp, lengthpos + 8 + zlength + 4, SEEK_SET); + return PNGERR_NONE; +} + + +/*------------------------------------------------- + convert_bitmap_to_image_palette - convert a + bitmap to a palettized image +-------------------------------------------------*/ + +static png_error convert_bitmap_to_image_palette(png_info *pnginfo, const bitmap_t *bitmap, int palette_length, const rgb_t *palette) +{ + int rowbytes; + int x, y; + + /* set the common info */ + pnginfo->width = bitmap->width; + pnginfo->height = bitmap->height; + pnginfo->bit_depth = 8; + pnginfo->color_type = 3; + pnginfo->num_palette = 256; + rowbytes = pnginfo->width; + + /* allocate memory for the palette */ + pnginfo->palette = malloc(3 * 256); + if (pnginfo->palette == NULL) + return PNGERR_OUT_OF_MEMORY; + + /* build the palette */ + memset(pnginfo->palette, 0, 3 * 256); + for (x = 0; x < palette_length; x++) + { + rgb_t color = palette[x]; + pnginfo->palette[3 * x + 0] = RGB_RED(color); + pnginfo->palette[3 * x + 1] = RGB_GREEN(color); + pnginfo->palette[3 * x + 2] = RGB_BLUE(color); + } + + /* allocate memory for the image */ + pnginfo->image = malloc(pnginfo->height * (rowbytes + 1)); + if (pnginfo->image == NULL) + { + free(pnginfo->palette); + return PNGERR_OUT_OF_MEMORY; + } + + /* copy in the pixels, specifying a NULL filter */ + for (y = 0; y < pnginfo->height; y++) + { + UINT16 *src = (UINT16 *)bitmap->base + y * bitmap->rowpixels; + UINT8 *dst = pnginfo->image + y * (rowbytes + 1); + + /* store the filter byte, then copy the data */ + *dst++ = 0; + for (x = 0; x < pnginfo->width; x++) + *dst++ = *src++; + } + + return PNGERR_NONE; +} + + +/*------------------------------------------------- + convert_bitmap_to_image_rgb - convert a + bitmap to an RGB image +-------------------------------------------------*/ + +static png_error convert_bitmap_to_image_rgb(png_info *pnginfo, const bitmap_t *bitmap, int palette_length, const rgb_t *palette) +{ + int alpha = (bitmap->format == BITMAP_FORMAT_ARGB32); + int rowbytes; + int x, y; + + /* set the common info */ + pnginfo->width = bitmap->width; + pnginfo->height = bitmap->height; + pnginfo->bit_depth = 8; + pnginfo->color_type = alpha ? 6 : 2; + rowbytes = pnginfo->width * (alpha ? 4 : 3); + + /* allocate memory for the image */ + pnginfo->image = malloc(pnginfo->height * (rowbytes + 1)); + if (pnginfo->image == NULL) + return PNGERR_OUT_OF_MEMORY; + + /* copy in the pixels, specifying a NULL filter */ + for (y = 0; y < pnginfo->height; y++) + { + UINT32 *src32 = BITMAP_ADDR32(bitmap, y, 0); + UINT16 *src16 = BITMAP_ADDR16(bitmap, y, 0); + UINT8 *dst = pnginfo->image + y * (rowbytes + 1); + + /* store the filter byte, then copy the data */ + *dst++ = 0; + + /* 16bpp palettized format */ + if (bitmap->format == BITMAP_FORMAT_INDEXED16) + { + for (x = 0; x < pnginfo->width; x++) + { + rgb_t color = palette[*src16++]; + *dst++ = RGB_RED(color); + *dst++ = RGB_GREEN(color); + *dst++ = RGB_BLUE(color); + } + } + + /* RGB formats */ + else if (bitmap->format == BITMAP_FORMAT_RGB15) + { + for (x = 0; x < pnginfo->width; x++) + { + UINT16 raw = *src16++; + *dst++ = pal5bit(raw >> 10); + *dst++ = pal5bit(raw >> 5); + *dst++ = pal5bit(raw >> 0); + } + } + + /* 32-bit RGB direct */ + else if (bitmap->format == BITMAP_FORMAT_RGB32) + { + for (x = 0; x < pnginfo->width; x++) + { + UINT32 raw = *src32++; + *dst++ = RGB_RED(raw); + *dst++ = RGB_GREEN(raw); + *dst++ = RGB_BLUE(raw); + } + } + + /* 32-bit ARGB direct */ + else if (bitmap->format == BITMAP_FORMAT_ARGB32) + { + for (x = 0; x < pnginfo->width; x++) + { + UINT32 raw = *src32++; + *dst++ = RGB_RED(raw); + *dst++ = RGB_GREEN(raw); + *dst++ = RGB_BLUE(raw); + *dst++ = RGB_ALPHA(raw); + } + } + + /* unsupported format */ + else + return PNGERR_UNSUPPORTED_FORMAT; + } + + return PNGERR_NONE; +} + + +/*------------------------------------------------- + write_png_stream - stream a series of PNG + chunks to the given file +-------------------------------------------------*/ + +static png_error write_png_stream(core_file *fp, png_info *pnginfo, const bitmap_t *bitmap, int palette_length, const rgb_t *palette) +{ + UINT8 tempbuff[16]; + png_text *text; + png_error error; + + /* create an unfiltered image in either palette or RGB form */ + if (bitmap->format == BITMAP_FORMAT_INDEXED16 && palette_length <= 256) + error = convert_bitmap_to_image_palette(pnginfo, bitmap, palette_length, palette); + else + error = convert_bitmap_to_image_rgb(pnginfo, bitmap, palette_length, palette); + if (error != PNGERR_NONE) + goto handle_error; + + /* if we wanted to get clever and do filtering, we would do it here */ + + /* write the IHDR chunk */ + put_32bit(tempbuff + 0, pnginfo->width); + put_32bit(tempbuff + 4, pnginfo->height); + put_8bit(tempbuff + 8, pnginfo->bit_depth); + put_8bit(tempbuff + 9, pnginfo->color_type); + put_8bit(tempbuff + 10, pnginfo->compression_method); + put_8bit(tempbuff + 11, pnginfo->filter_method); + put_8bit(tempbuff + 12, pnginfo->interlace_method); + error = write_chunk(fp, tempbuff, PNG_CN_IHDR, 13); + if (error != PNGERR_NONE) + goto handle_error; + + /* write the PLTE chunk */ + if (pnginfo->num_palette > 0) + error = write_chunk(fp, pnginfo->palette, PNG_CN_PLTE, pnginfo->num_palette * 3); + if (error != PNGERR_NONE) + goto handle_error; + + /* write a single IDAT chunk */ + error = write_deflated_chunk(fp, pnginfo->image, PNG_CN_IDAT, pnginfo->height * (compute_rowbytes(pnginfo) + 1)); + if (error != PNGERR_NONE) + goto handle_error; + + /* write TEXT chunks */ + for (text = pnginfo->textlist; text != NULL; text = text->next) + { + error = write_chunk(fp, (UINT8 *)text->keyword, PNG_CN_tEXt, (UINT32)strlen(text->keyword) + 1 + (UINT32)strlen(text->text)); + if (error != PNGERR_NONE) + goto handle_error; + } + + /* write an IEND chunk */ + error = write_chunk(fp, NULL, PNG_CN_IEND, 0); + +handle_error: + return error; +} + + + +png_error png_write_bitmap(core_file *fp, png_info *info, bitmap_t *bitmap, int palette_length, const UINT32 *palette) +{ + png_info pnginfo; + png_error error; + + /* use a dummy pnginfo if none passed to us */ + if (info == NULL) + { + info = &pnginfo; + memset(&pnginfo, 0, sizeof(pnginfo)); + } + + /* write the PNG signature */ + if (core_fwrite(fp, PNG_Signature, 8) != 8) + { + if (info == &pnginfo) + png_free(&pnginfo); + return PNGERR_FILE_ERROR; + } + + /* write the rest of the PNG data */ + error = write_png_stream(fp, info, bitmap, palette_length, palette); + if (info == &pnginfo) + png_free(&pnginfo); + return error; +} + + + +/******************************************************************************** + + MNG write functions + +********************************************************************************/ + +png_error mng_capture_start(core_file *fp, bitmap_t *bitmap, double rate) +{ + UINT8 mhdr[28]; + png_error error; + + if (core_fwrite(fp, MNG_Signature, 8) != 8) + return PNGERR_FILE_ERROR; + + memset(mhdr, 0, 28); + put_32bit(mhdr + 0, bitmap->width); + put_32bit(mhdr + 4, bitmap->height); + put_32bit(mhdr + 8, rate); + put_32bit(mhdr + 24, 0x0041); /* Simplicity profile */ + /* frame count and play time unspecified because + we don't know at this stage */ + error = write_chunk(fp, mhdr, MNG_CN_MHDR, 28); + if (error != PNGERR_NONE) + return error; + + return PNGERR_NONE; +} + +png_error mng_capture_frame(core_file *fp, png_info *info, bitmap_t *bitmap, int palette_length, const UINT32 *palette) +{ + return write_png_stream(fp, info, bitmap, palette_length, palette); +} + +png_error mng_capture_stop(core_file *fp) +{ + return write_chunk(fp, NULL, MNG_CN_MEND, 0); +} diff --git a/src/lib/util/png.h b/src/lib/util/png.h new file mode 100644 index 00000000000..9d11d895d64 --- /dev/null +++ b/src/lib/util/png.h @@ -0,0 +1,137 @@ +/*************************************************************************** + + png.h + + PNG file management. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#pragma once + +#ifndef __PNG_H__ +#define __PNG_H__ + +#include "osdcore.h" +#include "bitmap.h" +#include "corefile.h" + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define PNG_Signature "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A" +#define MNG_Signature "\x8A\x4D\x4E\x47\x0D\x0A\x1A\x0A" + +/* Chunk names */ +#define PNG_CN_IHDR 0x49484452L +#define PNG_CN_PLTE 0x504C5445L +#define PNG_CN_IDAT 0x49444154L +#define PNG_CN_IEND 0x49454E44L +#define PNG_CN_gAMA 0x67414D41L +#define PNG_CN_sBIT 0x73424954L +#define PNG_CN_cHRM 0x6348524DL +#define PNG_CN_tRNS 0x74524E53L +#define PNG_CN_bKGD 0x624B4744L +#define PNG_CN_hIST 0x68495354L +#define PNG_CN_tEXt 0x74455874L +#define PNG_CN_zTXt 0x7A545874L +#define PNG_CN_pHYs 0x70485973L +#define PNG_CN_oFFs 0x6F464673L +#define PNG_CN_tIME 0x74494D45L +#define PNG_CN_sCAL 0x7343414CL + +/* MNG Chunk names */ +#define MNG_CN_MHDR 0x4D484452L +#define MNG_CN_MEND 0x4D454E44L +#define MNG_CN_TERM 0x5445524DL +#define MNG_CN_BACK 0x4241434BL + +/* Prediction filters */ +#define PNG_PF_None 0 +#define PNG_PF_Sub 1 +#define PNG_PF_Up 2 +#define PNG_PF_Average 3 +#define PNG_PF_Paeth 4 + +/* Error types */ +enum _png_error +{ + PNGERR_NONE, + PNGERR_OUT_OF_MEMORY, + PNGERR_UNKNOWN_FILTER, + PNGERR_FILE_ERROR, + PNGERR_BAD_SIGNATURE, + PNGERR_DECOMPRESS_ERROR, + PNGERR_FILE_TRUNCATED, + PNGERR_FILE_CORRUPT, + PNGERR_UNKNOWN_CHUNK, + PNGERR_COMPRESS_ERROR, + PNGERR_UNSUPPORTED_FORMAT +}; +typedef enum _png_error png_error; + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +typedef struct _png_text png_text; +struct _png_text +{ + png_text * next; + const char * keyword; /* this is allocated */ + const char * text; /* this points to a part of keyword */ +}; + + +typedef struct _png_info png_info; +struct _png_info +{ + UINT8 * image; + UINT32 width, height; + UINT32 xres, yres; + rectangle screen; + double xscale, yscale; + double source_gamma; + UINT32 resolution_unit; + UINT8 bit_depth; + UINT8 color_type; + UINT8 compression_method; + UINT8 filter_method; + UINT8 interlace_method; + + UINT8 * palette; + UINT32 num_palette; + + UINT8 * trans; + UINT32 num_trans; + + png_text * textlist; +}; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +void png_free(png_info *pnginfo); + +png_error png_read_file(core_file *fp, png_info *pnginfo); +png_error png_read_bitmap(core_file *fp, bitmap_t **bitmap); +png_error png_expand_buffer_8bit(png_info *p); + +png_error png_add_text(png_info *pnginfo, const char *keyword, const char *text); +png_error png_write_bitmap(core_file *fp, png_info *info, bitmap_t *bitmap, int palette_length, const UINT32 *palette); + +png_error mng_capture_start(core_file *fp, bitmap_t *bitmap, double rate); +png_error mng_capture_frame(core_file *fp, png_info *info, bitmap_t *bitmap, int palette_length, const UINT32 *palette); +png_error mng_capture_stop(core_file *fp); + +#endif /* __PNG_H__ */ diff --git a/src/lib/util/pool.c b/src/lib/util/pool.c new file mode 100644 index 00000000000..62b131416fb --- /dev/null +++ b/src/lib/util/pool.c @@ -0,0 +1,614 @@ +/*************************************************************************** + + pool.c + + Resource pool code + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#include <stdarg.h> +#include <stddef.h> +#include "pool.h" +#include "coreutil.h" + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define OBJECT_ENTRY_BLOCK 256 +#define POOL_HASH_SIZE 3797 + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* an object type entry */ +typedef struct _objtype_entry objtype_entry; +struct _objtype_entry +{ + objtype_entry * next; + UINT32 type; + const char * friendly; + void (*destructor)(void *, size_t); +}; + + +/* an entry in a pool */ +typedef struct _object_entry object_entry; +struct _object_entry +{ + object_entry * next; + objtype_entry * type; + void * object; + size_t size; + const char * file; + int line; +}; + + +/* a block of entry items */ +typedef struct _object_entry_block object_entry_block; +struct _object_entry_block +{ + object_entry_block *next; + object_entry entry[OBJECT_ENTRY_BLOCK]; +}; + + +/* the object pool itself */ +struct _object_pool +{ + object_entry * hashtable[POOL_HASH_SIZE]; + object_entry * freelist; + object_entry_block *blocklist; + objtype_entry * typelist; + void (*fail)(const char *message); +}; + + +/* an iterator over objects in a pool */ +struct _object_pool_iterator +{ + object_pool * pool; + object_type type; + int hashnum; + object_entry * last; +}; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +static void memory_destruct(void *object, size_t size); +static void report_failure(object_pool *pool, const char *format, ...); + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + hash_object - compute the hash for a given + object +-------------------------------------------------*/ + +INLINE int hash_object(void *object) +{ + return ((size_t)object >> 4) % POOL_HASH_SIZE; +} + + +/*------------------------------------------------- + hash_object - compute the hash for a given + object +-------------------------------------------------*/ + +INLINE objtype_entry *get_object_type(object_pool *pool, object_type type) +{ + objtype_entry *entry; + + for (entry = pool->typelist; entry != NULL; entry = entry->next) + if (entry->type == type) + return entry; + + return NULL; +} + + + +/*************************************************************************** + RESOURCE POOL MANAGEMENT +***************************************************************************/ + +/*------------------------------------------------- + pool_alloc - allocates a new memory pool +-------------------------------------------------*/ + +object_pool *pool_alloc(void (*fail)(const char *message)) +{ + object_pool *pool; + + /* allocate memory for the pool itself */ + pool = malloc(sizeof(*pool)); + if (pool == NULL) + return NULL; + memset(pool, 0, sizeof(*pool)); + + /* register the built-in types */ + pool_type_register(pool, OBJTYPE_MEMORY, "Memory", memory_destruct); + + return pool; +} + + +/*------------------------------------------------- + pool_type_register - register a new pool + type +-------------------------------------------------*/ + +void pool_type_register(object_pool *pool, object_type type, const char *friendly, void (*destructor)(void *, size_t)) +{ + objtype_entry *newtype = get_object_type(pool, type); + + /* if the type doesn't already exist... */ + if (newtype == NULL) + { + /* allocate a new entry */ + newtype = malloc(sizeof(*newtype)); + if (newtype == NULL) + { + report_failure(pool, "Error adding new type %s\n", friendly); + return; + } + memset(newtype, 0, sizeof(*newtype)); + + /* link us in */ + newtype->next = pool->typelist; + pool->typelist = newtype; + } + + /* fill in the type information */ + newtype->type = type; + newtype->friendly = friendly; + newtype->destructor = destructor; +} + + +/*------------------------------------------------- + pool_clear - frees all contained memory blocks + in a pool +-------------------------------------------------*/ + +void pool_clear(object_pool *pool) +{ + int hashnum; + + /* iterate over hash buckets */ + for (hashnum = 0; hashnum < ARRAY_LENGTH(pool->hashtable); hashnum++) + { + object_entry *entry, *next; + + /* iterate over entries in this hash bucket and free them */ + for (entry = pool->hashtable[hashnum]; entry != NULL; entry = next) + { + /* remember the next entry */ + next = entry->next; + + /* call the destructor */ + (*entry->type->destructor)(entry->object, entry->size); + + /* add ourself to the free list */ + entry->next = pool->freelist; + pool->freelist = entry; + } + + /* reset the list we just walked */ + pool->hashtable[hashnum] = NULL; + } +} + + +/*------------------------------------------------- + pool_free - frees a memory pool and all + contained memory blocks +-------------------------------------------------*/ + +void pool_free(object_pool *pool) +{ + object_entry_block *block, *nextblock; + objtype_entry *type, *nexttype; + + /* clear the pool */ + pool_clear(pool); + + /* free all entry blocks */ + for (block = pool->blocklist; block != NULL; block = nextblock) + { + nextblock = block->next; + free(block); + } + + /* free all types */ + for (type = pool->typelist; type != NULL; type = nexttype) + { + nexttype = type->next; + free(type); + } + + /* free the pool itself */ + free(pool); +} + + + +/*************************************************************************** + OBJECT MANAGEMENT +***************************************************************************/ + +/*------------------------------------------------- + pool_object_add_file_line - add an object to + the resource pool +-------------------------------------------------*/ + +void *pool_object_add_file_line(object_pool *pool, object_type _type, void *object, size_t size, const char *file, int line) +{ + objtype_entry *type = get_object_type(pool, _type); + int hashnum = hash_object(object); + object_entry *entry; + + /* if we have an invalid type, fail */ + if (type == NULL) + { + report_failure(pool, "pool_object_add (via %s:%d): Attempted to add object of unknown type", file, line, (int)size); + return object; + } + + /* if we get a NULL object, fail */ + if (object == NULL) + { + report_failure(pool, "pool_object_add (via %s:%d): Attempted to add a NULL object of size %d", file, line, (int)size); + return object; + } + + /* allocate a new entry */ + if (pool->freelist == NULL) + { + object_entry_block *block; + int entrynum; + + /* if we need a new block, allocate that now */ + block = malloc(sizeof(*block)); + if (block == NULL) + return NULL; + memset(block, 0, sizeof(*block)); + + /* hook us into the blocklist */ + block->next = pool->blocklist; + pool->blocklist = block; + + /* add all entries to the free list */ + for (entrynum = 0; entrynum < ARRAY_LENGTH(block->entry); entrynum++) + { + block->entry[entrynum].next = pool->freelist; + pool->freelist = &block->entry[entrynum]; + } + } + + /* remove the head of the freelist */ + entry = pool->freelist; + pool->freelist = entry->next; + + /* fill in the entry */ + entry->type = type; + entry->object = object; + entry->size = size; + entry->file = file; + entry->line = line; + + /* hook us into the list */ + entry->next = pool->hashtable[hashnum]; + pool->hashtable[hashnum] = entry; + return object; +} + + +/*------------------------------------------------- + pool_object_remove - remove an entry from a + pool, optionally calling its destructor +-------------------------------------------------*/ + +void *pool_object_remove(object_pool *pool, void *object, int destruct) +{ + int hashnum = hash_object(object); + object_entry **entryptr; + + /* find the object in question and remove it */ + for (entryptr = &pool->hashtable[hashnum]; *entryptr != NULL; entryptr = &(*entryptr)->next) + if ((*entryptr)->object == object) + { + object_entry *entry = *entryptr; + + /* call the destructor */ + if (destruct) + (*entry->type->destructor)(entry->object, entry->size); + + /* remove us from the list */ + *entryptr = entry->next; + + /* add us to the free list */ + entry->next = pool->freelist; + pool->freelist = entry; + break; + } + + return NULL; +} + + +/*------------------------------------------------- + pool_object_exists - return TRUE if an + object exists in the pool +-------------------------------------------------*/ + +int pool_object_exists(object_pool *pool, object_type type, void *object) +{ + int hashnum = hash_object(object); + object_entry *entry; + + /* find the object in question */ + for (entry = pool->hashtable[hashnum]; entry != NULL; entry = entry->next) + if (entry->object == object && (type == OBJTYPE_WILDCARD || entry->type->type == type)) + return TRUE; + + return FALSE; +} + + + +/*************************************************************************** + OBJECT ITERATORS +***************************************************************************/ + +/*------------------------------------------------- + pool_iterate_begin - begin iterating over + objects in an object pool +-------------------------------------------------*/ + +object_pool_iterator *pool_iterate_begin(object_pool *pool, object_type type) +{ + object_pool_iterator *iter; + + /* allocate the iterator */ + iter = malloc(sizeof(*iter)); + if (iter == NULL) + return NULL; + memset(iter, 0, sizeof(*iter)); + + /* fill it in */ + iter->pool = pool; + iter->type = type; + iter->hashnum = 0; + iter->last = NULL; + return iter; +} + + +/*------------------------------------------------- + pool_iterate_next - get the next object in the + object pool +-------------------------------------------------*/ + +int pool_iterate_next(object_pool_iterator *iter, void **objectptr, size_t *sizeptr, object_type *typeptr) +{ + /* advance from the last one */ + while (iter->hashnum < ARRAY_LENGTH(iter->pool->hashtable)) + { + /* if the last entry is non-NULL, advance */ + if (iter->last == NULL) + iter->last = iter->pool->hashtable[iter->hashnum++]; + else + iter->last = iter->last->next; + + /* stop when we get one */ + if (iter->last != NULL) + { + if (objectptr != NULL) + *objectptr = iter->last; + if (sizeptr != NULL) + *sizeptr = iter->last->size; + if (typeptr != NULL) + *typeptr = iter->last->type->type; + return TRUE; + } + } + + /* nothing left */ + return FALSE; +} + + +/*------------------------------------------------- + pool_iterate_end - finish iterating over + objects in an object pool +-------------------------------------------------*/ + +void pool_iterate_end(object_pool_iterator *iter) +{ + free(iter); +} + + + +/*************************************************************************** + MEMORY HELPERS +***************************************************************************/ + +/*------------------------------------------------- + pool_malloc_file_line - allocates memory in a + memory pool +-------------------------------------------------*/ + +void *pool_malloc_file_line(object_pool *pool, size_t size, const char *file, int line) +{ +#ifdef MALLOC_DEBUG + void *ptr = malloc_file_line(size, file, line); +#else + void *ptr = malloc(size); +#endif + return pool_object_add_file_line(pool, OBJTYPE_MEMORY, ptr, size, file, line); +} + + +/*------------------------------------------------- + pool_realloc_file_line - reallocs memory in a + memory pool +-------------------------------------------------*/ + +void *pool_realloc_file_line(object_pool *pool, void *ptr, size_t size, const char *file, int line) +{ + if (ptr != NULL) + pool_object_remove(pool, ptr, FALSE); +#ifdef MALLOC_DEBUG + ptr = realloc_file_line(ptr, size, file, line); +#else + ptr = realloc(ptr, size); +#endif + if (size != 0) + pool_object_add_file_line(pool, OBJTYPE_MEMORY, ptr, size, file, line); + return ptr; +} + + +/*------------------------------------------------- + pool_strdup_file_line - strdup in a memory + pool +-------------------------------------------------*/ + +char *pool_strdup_file_line(object_pool *pool, const char *str, const char *file, int line) +{ + char *ptr = pool_malloc_file_line(pool, strlen(str) + 1, file, line); + if (ptr != NULL) + strcpy(ptr, str); + return ptr; +} + + + +/*************************************************************************** + GENERIC DESTRUCTORS +***************************************************************************/ + +/*------------------------------------------------- + memory_destruct - generic destructor + that just does a free() on the object +-------------------------------------------------*/ + +static void memory_destruct(void *object, size_t size) +{ + free(object); +} + + + +/*************************************************************************** + INTERNAL FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + report_failure - report a failure to the + failure callback +-------------------------------------------------*/ + +static void report_failure(object_pool *pool, const char *format, ...) +{ + /* only do the work if we have a callback */ + if (pool->fail != NULL) + { + char message[1024]; + va_list argptr; + + /* generate the final message text */ + va_start(argptr, format); + vsprintf(message, format, argptr); + va_end(argptr); + + /* call the callback with the message */ + (*pool->fail)(message); + } +} + + + +/*************************************************************************** + TESTING FUNCTIONS +***************************************************************************/ + +static int has_memory_error; + + +/*------------------------------------------------- + memory_error - report a memory error +-------------------------------------------------*/ + +static void memory_error(const char *message) +{ + printf("memory test failure: %s\n", message); + has_memory_error = TRUE; +} + + +/*------------------------------------------------- + test_memory_pools - unit tests for memory + pool behavior +-------------------------------------------------*/ + +int test_memory_pools(void) +{ + object_pool *pool; + void *ptrs[16]; + int i; + + has_memory_error = FALSE; + pool = pool_alloc(memory_error); + memset(ptrs, 0, sizeof(ptrs)); + + ptrs[0] = pool_malloc(pool, 50); + ptrs[1] = pool_malloc(pool, 100); + + ptrs[0] = pool_realloc(pool, ptrs[0], 150); + ptrs[1] = pool_realloc(pool, ptrs[1], 200); + + ptrs[2] = pool_malloc(pool, 250); + ptrs[3] = pool_malloc(pool, 300); + + ptrs[0] = pool_realloc(pool, ptrs[0], 350); + ptrs[1] = pool_realloc(pool, ptrs[1], 400); + + ptrs[2] = pool_realloc(pool, ptrs[2], 450); + ptrs[3] = pool_realloc(pool, ptrs[3], 500); + + ptrs[0] = pool_realloc(pool, ptrs[0], 0); + ptrs[1] = pool_realloc(pool, ptrs[1], 0); + + ptrs[2] = pool_realloc(pool, ptrs[2], 550); + ptrs[3] = pool_realloc(pool, ptrs[3], 600); + + /* some heavier stress tests */ + for (i = 0; i < 512; i++) + { + ptrs[i % ARRAY_LENGTH(ptrs)] = pool_realloc(pool, + ptrs[i % ARRAY_LENGTH(ptrs)], rand() % 1000); + } + + pool_free(pool); + return has_memory_error; +} diff --git a/src/lib/util/pool.h b/src/lib/util/pool.h new file mode 100644 index 00000000000..6d746c36de5 --- /dev/null +++ b/src/lib/util/pool.h @@ -0,0 +1,117 @@ +/*************************************************************************** + + pool.h + + Abstract object pool management + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#ifndef __POOL_H__ +#define __POOL_H__ + +#include "osdcore.h" + + +/*************************************************************************** + MACROS +***************************************************************************/ + +/* helper macros for memory pools that pass file/line number */ +#define pool_malloc(pool, size) pool_malloc_file_line((pool), (size), __FILE__, __LINE__) +#define pool_realloc(pool, ptr, size) pool_realloc_file_line((pool), (ptr), (size), __FILE__, __LINE__) +#define pool_strdup(pool, size) pool_strdup_file_line((pool), (size), __FILE__, __LINE__) + +/* macro to define a 4-character type for a pool */ +#define OBJECT_TYPE(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) + +/* built-in pool types */ +#define OBJTYPE_WILDCARD (0) +#define OBJTYPE_MEMORY OBJECT_TYPE('m','e','m','o') + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* pool types are UINT32s */ +typedef UINT32 object_type; + +/* opaque type representing a pool of objects */ +typedef struct _object_pool object_pool; + +/* opaque type representing an iterator over pool objects */ +typedef struct _object_pool_iterator object_pool_iterator; + + + +/*************************************************************************** + PROTOTYPES +***************************************************************************/ + + +/* ----- object pool management ----- */ + +/* allocate a new object pool */ +object_pool *pool_alloc(void (*fail)(const char *message)); + +/* register a new object type; returns TRUE if the type already existed and was overridden */ +void pool_type_register(object_pool *pool, object_type type, const char *friendly, void (*destructor)(void *, size_t)); + +/* free all allocated objects in a pool */ +void pool_clear(object_pool *pool); + +/* free an object pool, including all allocated objects */ +void pool_free(object_pool *pool); + + + +/* ----- object management ----- */ + +/* add an object to the pool, along with its filename/line number */ +void *pool_object_add_file_line(object_pool *pool, object_type type, void *object, size_t size, const char *file, int line); + +/* remove an object from the pool (optionally calling destructor) */ +void *pool_object_remove(object_pool *pool, void *object, int destruct); + +/* does an object exist in the pool? */ +int pool_object_exists(object_pool *pool, object_type type, void *object); + + + +/* ----- object iterators ----- */ + +/* begin iterating over objects in an object pool */ +object_pool_iterator *pool_iterate_begin(object_pool *pool, object_type type); + +/* get the next object in the object pool */ +int pool_iterate_next(object_pool_iterator *iter, void **objectptr, size_t *sizeptr, object_type *typeptr); + +/* finish iterating over objects in an object pool */ +void pool_iterate_end(object_pool_iterator *iter); + + + +/* ----- memory helpers ----- */ + +/* malloc memory and register it with the given pool */ +void *pool_malloc_file_line(object_pool *pool, size_t size, const char *file, int line); + +/* realloc memory and register it with the given pool */ +void *pool_realloc_file_line(object_pool *pool, void *ptr, size_t size, const char *file, int line); + +/* strdup memory and register it with the given pool */ +char *pool_strdup_file_line(object_pool *pool, const char *str, const char *file, int line); + + + +/* ----- miscellaneous ----- */ + +/* internal unit tests */ +int test_memory_pools(void); + + +#endif /* __POOL_H__ */ diff --git a/src/lib/util/sha1.c b/src/lib/util/sha1.c new file mode 100644 index 00000000000..bf376874816 --- /dev/null +++ b/src/lib/util/sha1.c @@ -0,0 +1,387 @@ +/* sha1.h + * + * The sha1 hash function. + */ + +/* nettle, low-level cryptographics library + * + * Copyright (C) 2001 Peter Gutmann, Andrew Kuchling, Niels Moeller + * + * The nettle library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The nettle library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the nettle library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "sha1.h" +//#include "macros.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +static unsigned int READ_UINT32(const UINT8* data) +{ + return ((UINT32)data[0] << 24) | + ((UINT32)data[1] << 16) | + ((UINT32)data[2] << 8) | + ((UINT32)data[3]); +} + +static void WRITE_UINT32(unsigned char* data, UINT32 val) +{ + data[0] = (val >> 24) & 0xFF; + data[1] = (val >> 16) & 0xFF; + data[2] = (val >> 8) & 0xFF; + data[3] = (val >> 0) & 0xFF; +} + + +/* A block, treated as a sequence of 32-bit words. */ +#define SHA1_DATA_LENGTH 16 + +/* The SHA f()-functions. The f1 and f3 functions can be optimized to + save one boolean operation each - thanks to Rich Schroeppel, + rcs@cs.arizona.edu for discovering this */ + +/* #define f1(x,y,z) ( ( x & y ) | ( ~x & z ) ) Rounds 0-19 */ +#define f1(x,y,z) ( z ^ ( x & ( y ^ z ) ) ) /* Rounds 0-19 */ +#define f2(x,y,z) ( x ^ y ^ z ) /* Rounds 20-39 */ +/* #define f3(x,y,z) ( ( x & y ) | ( x & z ) | ( y & z ) ) Rounds 40-59 */ +#define f3(x,y,z) ( ( x & y ) | ( z & ( x | y ) ) ) /* Rounds 40-59 */ +#define f4(x,y,z) ( x ^ y ^ z ) /* Rounds 60-79 */ + +/* The SHA Mysterious Constants */ + +#define K1 0x5A827999L /* Rounds 0-19 */ +#define K2 0x6ED9EBA1L /* Rounds 20-39 */ +#define K3 0x8F1BBCDCL /* Rounds 40-59 */ +#define K4 0xCA62C1D6L /* Rounds 60-79 */ + +/* SHA initial values */ + +#define h0init 0x67452301L +#define h1init 0xEFCDAB89L +#define h2init 0x98BADCFEL +#define h3init 0x10325476L +#define h4init 0xC3D2E1F0L + +/* 32-bit rotate left - kludged with shifts */ +#ifdef _MSC_VER +#define ROTL(n,X) _lrotl(X, n) +#else +#define ROTL(n,X) ( ( (X) << (n) ) | ( (X) >> ( 32 - (n) ) ) ) +#endif + +/* The initial expanding function. The hash function is defined over an + 80-word expanded input array W, where the first 16 are copies of the input + data, and the remaining 64 are defined by + + W[ i ] = W[ i - 16 ] ^ W[ i - 14 ] ^ W[ i - 8 ] ^ W[ i - 3 ] + + This implementation generates these values on the fly in a circular + buffer - thanks to Colin Plumb, colin@nyx10.cs.du.edu for this + optimization. + + The updated SHA changes the expanding function by adding a rotate of 1 + bit. Thanks to Jim Gillogly, jim@rand.org, and an anonymous contributor + for this information */ + +#define expand(W,i) ( W[ i & 15 ] = \ + ROTL( 1, ( W[ i & 15 ] ^ W[ (i - 14) & 15 ] ^ \ + W[ (i - 8) & 15 ] ^ W[ (i - 3) & 15 ] ) ) ) + + +/* The prototype SHA sub-round. The fundamental sub-round is: + + a' = e + ROTL( 5, a ) + f( b, c, d ) + k + data; + b' = a; + c' = ROTL( 30, b ); + d' = c; + e' = d; + + but this is implemented by unrolling the loop 5 times and renaming the + variables ( e, a, b, c, d ) = ( a', b', c', d', e' ) each iteration. + This code is then replicated 20 times for each of the 4 functions, using + the next 20 values from the W[] array each time */ + +#define subRound(a, b, c, d, e, f, k, data) \ + ( e += ROTL( 5, a ) + f( b, c, d ) + k + data, b = ROTL( 30, b ) ) + +/* Initialize the SHA values */ + +void +sha1_init(struct sha1_ctx *ctx) +{ + /* Set the h-vars to their initial values */ + ctx->digest[ 0 ] = h0init; + ctx->digest[ 1 ] = h1init; + ctx->digest[ 2 ] = h2init; + ctx->digest[ 3 ] = h3init; + ctx->digest[ 4 ] = h4init; + + /* Initialize bit count */ + ctx->count_low = ctx->count_high = 0; + + /* Initialize buffer */ + ctx->index = 0; +} + +/* Perform the SHA transformation. Note that this code, like MD5, seems to + break some optimizing compilers due to the complexity of the expressions + and the size of the basic block. It may be necessary to split it into + sections, e.g. based on the four subrounds + + Note that this function destroys the data area */ + +static void +sha1_transform(UINT32 *state, UINT32 *data) +{ + UINT32 A, B, C, D, E; /* Local vars */ + + /* Set up first buffer and local data buffer */ + A = state[0]; + B = state[1]; + C = state[2]; + D = state[3]; + E = state[4]; + + /* Heavy mangling, in 4 sub-rounds of 20 interations each. */ + subRound( A, B, C, D, E, f1, K1, data[ 0] ); + subRound( E, A, B, C, D, f1, K1, data[ 1] ); + subRound( D, E, A, B, C, f1, K1, data[ 2] ); + subRound( C, D, E, A, B, f1, K1, data[ 3] ); + subRound( B, C, D, E, A, f1, K1, data[ 4] ); + subRound( A, B, C, D, E, f1, K1, data[ 5] ); + subRound( E, A, B, C, D, f1, K1, data[ 6] ); + subRound( D, E, A, B, C, f1, K1, data[ 7] ); + subRound( C, D, E, A, B, f1, K1, data[ 8] ); + subRound( B, C, D, E, A, f1, K1, data[ 9] ); + subRound( A, B, C, D, E, f1, K1, data[10] ); + subRound( E, A, B, C, D, f1, K1, data[11] ); + subRound( D, E, A, B, C, f1, K1, data[12] ); + subRound( C, D, E, A, B, f1, K1, data[13] ); + subRound( B, C, D, E, A, f1, K1, data[14] ); + subRound( A, B, C, D, E, f1, K1, data[15] ); + subRound( E, A, B, C, D, f1, K1, expand( data, 16 ) ); + subRound( D, E, A, B, C, f1, K1, expand( data, 17 ) ); + subRound( C, D, E, A, B, f1, K1, expand( data, 18 ) ); + subRound( B, C, D, E, A, f1, K1, expand( data, 19 ) ); + + subRound( A, B, C, D, E, f2, K2, expand( data, 20 ) ); + subRound( E, A, B, C, D, f2, K2, expand( data, 21 ) ); + subRound( D, E, A, B, C, f2, K2, expand( data, 22 ) ); + subRound( C, D, E, A, B, f2, K2, expand( data, 23 ) ); + subRound( B, C, D, E, A, f2, K2, expand( data, 24 ) ); + subRound( A, B, C, D, E, f2, K2, expand( data, 25 ) ); + subRound( E, A, B, C, D, f2, K2, expand( data, 26 ) ); + subRound( D, E, A, B, C, f2, K2, expand( data, 27 ) ); + subRound( C, D, E, A, B, f2, K2, expand( data, 28 ) ); + subRound( B, C, D, E, A, f2, K2, expand( data, 29 ) ); + subRound( A, B, C, D, E, f2, K2, expand( data, 30 ) ); + subRound( E, A, B, C, D, f2, K2, expand( data, 31 ) ); + subRound( D, E, A, B, C, f2, K2, expand( data, 32 ) ); + subRound( C, D, E, A, B, f2, K2, expand( data, 33 ) ); + subRound( B, C, D, E, A, f2, K2, expand( data, 34 ) ); + subRound( A, B, C, D, E, f2, K2, expand( data, 35 ) ); + subRound( E, A, B, C, D, f2, K2, expand( data, 36 ) ); + subRound( D, E, A, B, C, f2, K2, expand( data, 37 ) ); + subRound( C, D, E, A, B, f2, K2, expand( data, 38 ) ); + subRound( B, C, D, E, A, f2, K2, expand( data, 39 ) ); + + subRound( A, B, C, D, E, f3, K3, expand( data, 40 ) ); + subRound( E, A, B, C, D, f3, K3, expand( data, 41 ) ); + subRound( D, E, A, B, C, f3, K3, expand( data, 42 ) ); + subRound( C, D, E, A, B, f3, K3, expand( data, 43 ) ); + subRound( B, C, D, E, A, f3, K3, expand( data, 44 ) ); + subRound( A, B, C, D, E, f3, K3, expand( data, 45 ) ); + subRound( E, A, B, C, D, f3, K3, expand( data, 46 ) ); + subRound( D, E, A, B, C, f3, K3, expand( data, 47 ) ); + subRound( C, D, E, A, B, f3, K3, expand( data, 48 ) ); + subRound( B, C, D, E, A, f3, K3, expand( data, 49 ) ); + subRound( A, B, C, D, E, f3, K3, expand( data, 50 ) ); + subRound( E, A, B, C, D, f3, K3, expand( data, 51 ) ); + subRound( D, E, A, B, C, f3, K3, expand( data, 52 ) ); + subRound( C, D, E, A, B, f3, K3, expand( data, 53 ) ); + subRound( B, C, D, E, A, f3, K3, expand( data, 54 ) ); + subRound( A, B, C, D, E, f3, K3, expand( data, 55 ) ); + subRound( E, A, B, C, D, f3, K3, expand( data, 56 ) ); + subRound( D, E, A, B, C, f3, K3, expand( data, 57 ) ); + subRound( C, D, E, A, B, f3, K3, expand( data, 58 ) ); + subRound( B, C, D, E, A, f3, K3, expand( data, 59 ) ); + + subRound( A, B, C, D, E, f4, K4, expand( data, 60 ) ); + subRound( E, A, B, C, D, f4, K4, expand( data, 61 ) ); + subRound( D, E, A, B, C, f4, K4, expand( data, 62 ) ); + subRound( C, D, E, A, B, f4, K4, expand( data, 63 ) ); + subRound( B, C, D, E, A, f4, K4, expand( data, 64 ) ); + subRound( A, B, C, D, E, f4, K4, expand( data, 65 ) ); + subRound( E, A, B, C, D, f4, K4, expand( data, 66 ) ); + subRound( D, E, A, B, C, f4, K4, expand( data, 67 ) ); + subRound( C, D, E, A, B, f4, K4, expand( data, 68 ) ); + subRound( B, C, D, E, A, f4, K4, expand( data, 69 ) ); + subRound( A, B, C, D, E, f4, K4, expand( data, 70 ) ); + subRound( E, A, B, C, D, f4, K4, expand( data, 71 ) ); + subRound( D, E, A, B, C, f4, K4, expand( data, 72 ) ); + subRound( C, D, E, A, B, f4, K4, expand( data, 73 ) ); + subRound( B, C, D, E, A, f4, K4, expand( data, 74 ) ); + subRound( A, B, C, D, E, f4, K4, expand( data, 75 ) ); + subRound( E, A, B, C, D, f4, K4, expand( data, 76 ) ); + subRound( D, E, A, B, C, f4, K4, expand( data, 77 ) ); + subRound( C, D, E, A, B, f4, K4, expand( data, 78 ) ); + subRound( B, C, D, E, A, f4, K4, expand( data, 79 ) ); + + /* Build message digest */ + state[0] += A; + state[1] += B; + state[2] += C; + state[3] += D; + state[4] += E; +} + +static void +sha1_block(struct sha1_ctx *ctx, const UINT8 *block) +{ + UINT32 data[SHA1_DATA_LENGTH]; + int i; + + /* Update block count */ + if (!++ctx->count_low) + ++ctx->count_high; + + /* Endian independent conversion */ + for (i = 0; i<SHA1_DATA_LENGTH; i++, block += 4) + data[i] = READ_UINT32(block); + + sha1_transform(ctx->digest, data); +} + +void +sha1_update(struct sha1_ctx *ctx, + unsigned length, const UINT8 *buffer) +{ + if (ctx->index) + { /* Try to fill partial block */ + unsigned left = SHA1_DATA_SIZE - ctx->index; + if (length < left) + { + memcpy(ctx->block + ctx->index, buffer, length); + ctx->index += length; + return; /* Finished */ + } + else + { + memcpy(ctx->block + ctx->index, buffer, left); + sha1_block(ctx, ctx->block); + buffer += left; + length -= left; + } + } + while (length >= SHA1_DATA_SIZE) + { + sha1_block(ctx, buffer); + buffer += SHA1_DATA_SIZE; + length -= SHA1_DATA_SIZE; + } + if ((ctx->index = length)) /* This assignment is intended */ + /* Buffer leftovers */ + memcpy(ctx->block, buffer, length); +} + +/* Final wrapup - pad to SHA1_DATA_SIZE-byte boundary with the bit pattern + 1 0* (64-bit count of bits processed, MSB-first) */ + +void +sha1_final(struct sha1_ctx *ctx) +{ + UINT32 data[SHA1_DATA_LENGTH]; + int i; + int words; + + i = ctx->index; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + + assert(i < SHA1_DATA_SIZE); + ctx->block[i++] = 0x80; + + /* Fill rest of word */ + for( ; i & 3; i++) + ctx->block[i] = 0; + + /* i is now a multiple of the word size 4 */ + words = i >> 2; + for (i = 0; i < words; i++) + data[i] = READ_UINT32(ctx->block + 4*i); + + if (words > (SHA1_DATA_LENGTH-2)) + { /* No room for length in this block. Process it and + * pad with another one */ + for (i = words ; i < SHA1_DATA_LENGTH; i++) + data[i] = 0; + sha1_transform(ctx->digest, data); + for (i = 0; i < (SHA1_DATA_LENGTH-2); i++) + data[i] = 0; + } + else + for (i = words ; i < SHA1_DATA_LENGTH - 2; i++) + data[i] = 0; + + /* There are 512 = 2^9 bits in one block */ + data[SHA1_DATA_LENGTH-2] = (ctx->count_high << 9) | (ctx->count_low >> 23); + data[SHA1_DATA_LENGTH-1] = (ctx->count_low << 9) | (ctx->index << 3); + sha1_transform(ctx->digest, data); +} + +void +sha1_digest(const struct sha1_ctx *ctx, + unsigned length, + UINT8 *digest) +{ + unsigned i; + unsigned words; + unsigned leftover; + + assert(length <= SHA1_DIGEST_SIZE); + + words = length / 4; + leftover = length % 4; + + for (i = 0; i < words; i++, digest += 4) + WRITE_UINT32(digest, ctx->digest[i]); + + if (leftover) + { + UINT32 word; + unsigned j = leftover; + + assert(i < _SHA1_DIGEST_LENGTH); + + word = ctx->digest[i]; + + switch (leftover) + { + default: + /* this is just here to keep the compiler happy; it can never happen */ + case 3: + digest[--j] = (word >> 8) & 0xff; + /* Fall through */ + case 2: + digest[--j] = (word >> 16) & 0xff; + /* Fall through */ + case 1: + digest[--j] = (word >> 24) & 0xff; + } + } +} diff --git a/src/lib/util/sha1.h b/src/lib/util/sha1.h new file mode 100644 index 00000000000..8f9d9cdd835 --- /dev/null +++ b/src/lib/util/sha1.h @@ -0,0 +1,61 @@ +/* sha1.h + * + * The sha1 hash function. + */ + +/* nettle, low-level cryptographics library + * + * Copyright (C) 2001 Niels Moeller + * + * The nettle library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The nettle library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the nettle library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#ifndef NETTLE_SHA1_H_INCLUDED +#define NETTLE_SHA1_H_INCLUDED + +#include "osdcore.h" + +#define SHA1_DIGEST_SIZE 20 +#define SHA1_DATA_SIZE 64 + +/* Digest is kept internally as 4 32-bit words. */ +#define _SHA1_DIGEST_LENGTH 5 + +struct sha1_ctx +{ + UINT32 digest[_SHA1_DIGEST_LENGTH]; /* Message digest */ + UINT32 count_low, count_high; /* 64-bit block count */ + UINT8 block[SHA1_DATA_SIZE]; /* SHA1 data buffer */ + unsigned int index; /* index into buffer */ +}; + +void +sha1_init(struct sha1_ctx *ctx); + +void +sha1_update(struct sha1_ctx *ctx, + unsigned length, + const UINT8 *data); + +void +sha1_final(struct sha1_ctx *ctx); + +void +sha1_digest(const struct sha1_ctx *ctx, + unsigned length, + UINT8 *digest); + +#endif /* NETTLE_SHA1_H_INCLUDED */ diff --git a/src/lib/util/unicode.c b/src/lib/util/unicode.c new file mode 100644 index 00000000000..33dac112fa7 --- /dev/null +++ b/src/lib/util/unicode.c @@ -0,0 +1,346 @@ +/********************************************************************* + + unicode.c + + Unicode related functions + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +*********************************************************************/ + +#include "unicode.h" + + +/*------------------------------------------------- + uchar_isvalid - return true if a given + character is a legitimate unicode character +-------------------------------------------------*/ + +int uchar_isvalid(unicode_char uchar) +{ + return (uchar < 0x110000) && !((uchar >= 0xd800) && (uchar <= 0xdfff)); +} + + +/*------------------------------------------------- + uchar_from_utf8 - convert a UTF-8 sequence + into a unicode character +-------------------------------------------------*/ + +int uchar_from_utf8(unicode_char *uchar, const char *utf8char, size_t count) +{ + unicode_char c, minchar; + int auxlen, i; + char auxchar; + + /* validate parameters */ + if (utf8char == NULL || count == 0) + return 0; + + /* start with the first byte */ + c = (unsigned char) *utf8char; + count--; + utf8char++; + + /* based on that, determine how many additional bytes we need */ + if (c < 0x80) + { + /* unicode char 0x00000000 - 0x0000007F */ + c &= 0x7f; + auxlen = 0; + minchar = 0x00000000; + } + else if (c >= 0xc0 && c < 0xe0) + { + /* unicode char 0x00000080 - 0x000007FF */ + c &= 0x1f; + auxlen = 1; + minchar = 0x00000080; + } + else if (c >= 0xe0 && c < 0xf0) + { + /* unicode char 0x00000800 - 0x0000FFFF */ + c &= 0x0f; + auxlen = 2; + minchar = 0x00000800; + } + else if (c >= 0xf0 && c < 0xf8) + { + /* unicode char 0x00010000 - 0x001FFFFF */ + c &= 0x07; + auxlen = 3; + minchar = 0x00010000; + } + else if (c >= 0xf8 && c < 0xfc) + { + /* unicode char 0x00200000 - 0x03FFFFFF */ + c &= 0x03; + auxlen = 4; + minchar = 0x00200000; + } + else if (c >= 0xfc && c < 0xfe) + { + /* unicode char 0x04000000 - 0x7FFFFFFF */ + c &= 0x01; + auxlen = 5; + minchar = 0x04000000; + } + else + { + /* invalid */ + return -1; + } + + /* exceeds the count? */ + if (auxlen > count) + return -1; + + /* we now know how long the char is, now compute it */ + for (i = 0; i < auxlen; i++) + { + auxchar = utf8char[i]; + + /* all auxillary chars must be between 0x80-0xbf */ + if ((auxchar & 0xc0) != 0x80) + return -1; + + c = c << 6; + c |= auxchar & 0x3f; + } + + /* make sure that this char is above the minimum */ + if (c < minchar) + return -1; + + *uchar = c; + return auxlen + 1; +} + + +/*------------------------------------------------- + uchar_from_utf16 - convert a UTF-16 sequence + into a unicode character +-------------------------------------------------*/ + +int uchar_from_utf16(unicode_char *uchar, const utf16_char *utf16char, size_t count) +{ + int rc = -1; + + /* validate parameters */ + if (utf16char == NULL || count == 0) + return 0; + + /* handle the two-byte case */ + if (utf16char[0] >= 0xd800 && utf16char[0] <= 0xdbff) + { + if (count > 1 && utf16char[1] >= 0xdc00 && utf16char[1] <= 0xdfff) + { + *uchar = 0x10000 + ((utf16char[0] & 0x3ff) * 0x400) + (utf16char[1] & 0x3ff); + rc = 2; + } + } + + /* handle the one-byte case */ + else if (utf16char[0] < 0xdc00 || utf16char[0] > 0xdfff) + { + *uchar = utf16char[0]; + rc = 1; + } + + return rc; +} + + +/*------------------------------------------------- + uchar_from_utf16f - convert a UTF-16 sequence + into a unicode character from a flipped + byte order +-------------------------------------------------*/ + +int uchar_from_utf16f(unicode_char *uchar, const utf16_char *utf16char, size_t count) +{ + utf16_char buf[2] = {0}; + if (count > 0) + buf[0] = FLIPENDIAN_INT16(utf16char[0]); + if (count > 1) + buf[1] = FLIPENDIAN_INT16(utf16char[1]); + return uchar_from_utf16(uchar, buf, count); +} + + +/*------------------------------------------------- + utf8_from_uchar - convert a unicode character + into a UTF-8 sequence +-------------------------------------------------*/ + +int utf8_from_uchar(char *utf8string, size_t count, unicode_char uchar) +{ + int rc = 0; + + /* error on invalid characters */ + if (!uchar_isvalid(uchar)) + return -1; + + /* based on the value, output the appropriate number of bytes */ + if (uchar < 0x80) + { + /* unicode char 0x00000000 - 0x0000007F */ + if (count < 1) + return -1; + utf8string[rc++] = (char) uchar; + } + else if (uchar < 0x800) + { + /* unicode char 0x00000080 - 0x000007FF */ + if (count < 2) + return -1; + utf8string[rc++] = ((char) (uchar >> 6)) | 0xC0; + utf8string[rc++] = ((char) ((uchar >> 0) & 0x3F)) | 0x80; + } + else if (uchar < 0x10000) + { + /* unicode char 0x00000800 - 0x0000FFFF */ + if (count < 3) + return -1; + utf8string[rc++] = ((char) (uchar >> 12)) | 0xE0; + utf8string[rc++] = ((char) ((uchar >> 6) & 0x3F)) | 0x80; + utf8string[rc++] = ((char) ((uchar >> 0) & 0x3F)) | 0x80; + } + else if (uchar < 0x00200000) + { + /* unicode char 0x00010000 - 0x001FFFFF */ + if (count < 4) + return -1; + utf8string[rc++] = ((char) (uchar >> 18)) | 0xF0; + utf8string[rc++] = ((char) ((uchar >> 12) & 0x3F)) | 0x80; + utf8string[rc++] = ((char) ((uchar >> 6) & 0x3F)) | 0x80; + utf8string[rc++] = ((char) ((uchar >> 0) & 0x3F)) | 0x80; + } + else if (uchar < 0x04000000) + { + /* unicode char 0x00200000 - 0x03FFFFFF */ + if (count < 5) + return -1; + utf8string[rc++] = ((char) (uchar >> 24)) | 0xF8; + utf8string[rc++] = ((char) ((uchar >> 18) & 0x3F)) | 0x80; + utf8string[rc++] = ((char) ((uchar >> 12) & 0x3F)) | 0x80; + utf8string[rc++] = ((char) ((uchar >> 6) & 0x3F)) | 0x80; + utf8string[rc++] = ((char) ((uchar >> 0) & 0x3F)) | 0x80; + } + else if (uchar < 0x80000000) + { + /* unicode char 0x04000000 - 0x7FFFFFFF */ + if (count < 6) + return -1; + utf8string[rc++] = ((char) (uchar >> 30)) | 0xFC; + utf8string[rc++] = ((char) ((uchar >> 24) & 0x3F)) | 0x80; + utf8string[rc++] = ((char) ((uchar >> 18) & 0x3F)) | 0x80; + utf8string[rc++] = ((char) ((uchar >> 12) & 0x3F)) | 0x80; + utf8string[rc++] = ((char) ((uchar >> 6) & 0x3F)) | 0x80; + utf8string[rc++] = ((char) ((uchar >> 0) & 0x3F)) | 0x80; + } + else + rc = -1; + + return rc; +} + + +/*------------------------------------------------- + utf16_from_uchar - convert a unicode character + into a UTF-16 sequence +-------------------------------------------------*/ + +int utf16_from_uchar(utf16_char *utf16string, size_t count, unicode_char uchar) +{ + int rc; + + /* error on invalid characters */ + if (!uchar_isvalid(uchar)) + return -1; + + /* single word case */ + if (uchar < 0x10000) + { + if (count < 1) + return -1; + utf16string[0] = (utf16_char) uchar; + rc = 1; + } + + /* double word case */ + else if (uchar < 0x100000) + { + if (count < 2) + return -1; + utf16string[0] = ((uchar >> 10) & 0x03ff) | 0xd800; + utf16string[1] = ((uchar >> 0) & 0x03ff) | 0xdc00; + rc = 2; + } + else + return -1; + return rc; +} + + +/*------------------------------------------------- + utf16_from_uchar - convert a unicode character + into a UTF-16 sequence with flipped endianness +-------------------------------------------------*/ + +int utf16f_from_uchar(utf16_char *utf16string, size_t count, unicode_char uchar) +{ + int rc; + utf16_char buf[2] = { 0, 0 }; + + rc = utf16_from_uchar(buf, count, uchar); + + if (rc >= 1) + utf16string[0] = FLIPENDIAN_INT16(buf[0]); + if (rc >= 2) + utf16string[1] = FLIPENDIAN_INT16(buf[1]); + return rc; +} + + +/*------------------------------------------------- + utf8_previous_char - return a pointer to the + previous character in a string +-------------------------------------------------*/ + +const char *utf8_previous_char(const char *utf8string) +{ + while ((*--utf8string & 0xc0) == 0x80) + ; + return utf8string; +} + + +/*------------------------------------------------- + utf8_is_valid_string - return true if the + given string is a properly formed sequence of + UTF-8 characters +-------------------------------------------------*/ + +int utf8_is_valid_string(const char *utf8string) +{ + int remaining_length = strlen(utf8string); + + while (*utf8string != 0) + { + unicode_char uchar = 0; + int charlen; + + /* extract the current character and verify it */ + charlen = uchar_from_utf8(&uchar, utf8string, remaining_length); + if (charlen <= 0 || uchar == 0 || !uchar_isvalid(uchar)) + return FALSE; + + /* advance */ + utf8string += charlen; + remaining_length -= charlen; + } + + return TRUE; +} diff --git a/src/lib/util/unicode.h b/src/lib/util/unicode.h new file mode 100644 index 00000000000..19eb3eab9e0 --- /dev/null +++ b/src/lib/util/unicode.h @@ -0,0 +1,92 @@ +/********************************************************************* + + unicode.h + + Unicode related functions + + This code is for converting between UTF-8, UTF-16, and 32-bit + Unicode strings. These functions roughly parallel C runtime + library functions like mbtowc() and similar functions, but are + specific for these Unicode encodings. Specifically, there are + functions that convert UTF-8 and UTF-16 char clusters to and from + singular 32-bit Unicode chars. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +*********************************************************************/ + +#pragma once + +#ifndef UNICODE_H +#define UNICODE_H + +#include <stdlib.h> +#include "osdcore.h" + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +/* these defines specify the maximum size of different types of Unicode + * character encodings */ +#define UTF8_CHAR_MAX 6 +#define UTF16_CHAR_MAX 2 + +/* these are UTF-8 encoded strings for common characters */ +#define UTF8_NBSP "\xc2\xa0" /* non-breaking space */ +#define UTF8_MULTIPLY "\xc3\x97" /* multiplication symbol */ + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +typedef UINT16 utf16_char; +typedef UINT32 unicode_char; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +/* tests to see if a unicode char is a valid code point */ +int uchar_isvalid(unicode_char uchar); + +/* converting strings to 32-bit Unicode chars */ +int uchar_from_utf8(unicode_char *uchar, const char *utf8char, size_t count); +int uchar_from_utf16(unicode_char *uchar, const utf16_char *utf16char, size_t count); +int uchar_from_utf16f(unicode_char *uchar, const utf16_char *utf16char, size_t count); + +/* converting 32-bit Unicode chars to strings */ +int utf8_from_uchar(char *utf8string, size_t count, unicode_char uchar); +int utf16_from_uchar(utf16_char *utf16string, size_t count, unicode_char uchar); +int utf16f_from_uchar(utf16_char *utf16string, size_t count, unicode_char uchar); + +/* misc UTF-8 helpers */ +const char *utf8_previous_char(const char *utf8string); +int utf8_is_valid_string(const char *utf8string); + + + +/*************************************************************************** + MACROS +***************************************************************************/ + +#ifdef LSB_FIRST +#define uchar_from_utf16be uchar_from_utf16f +#define uchar_from_utf16le uchar_from_utf16 +#define utf16be_from_uchar utf16f_from_uchar +#define utf16le_from_uchar utf16_from_uchar +#else +#define uchar_from_utf16be uchar_from_utf16 +#define uchar_from_utf16le uchar_from_utf16f +#define utf16be_from_uchar utf16_from_uchar +#define utf16le_from_uchar utf16f_from_uchar +#endif + +#endif /* UNICODE_H */ diff --git a/src/lib/util/unzip.c b/src/lib/util/unzip.c new file mode 100644 index 00000000000..9b95321ded7 --- /dev/null +++ b/src/lib/util/unzip.c @@ -0,0 +1,616 @@ +/*************************************************************************** + + unzip.c + + Functions to manipulate data within ZIP files. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#include "osdcore.h" +#include "unzip.h" + +#include <ctype.h> +#include <zlib.h> + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +/* number of open files to cache */ +#define ZIP_CACHE_SIZE 8 + +/* offsets in end of central directory structure */ +#define ZIPESIG 0x00 +#define ZIPEDSK 0x04 +#define ZIPECEN 0x06 +#define ZIPENUM 0x08 +#define ZIPECENN 0x0a +#define ZIPECSZ 0x0c +#define ZIPEOFST 0x10 +#define ZIPECOML 0x14 +#define ZIPECOM 0x16 + +/* offsets in central directory entry structure */ +#define ZIPCENSIG 0x00 +#define ZIPCVER 0x04 +#define ZIPCOS 0x05 +#define ZIPCVXT 0x06 +#define ZIPCEXOS 0x07 +#define ZIPCFLG 0x08 +#define ZIPCMTHD 0x0a +#define ZIPCTIM 0x0c +#define ZIPCDAT 0x0e +#define ZIPCCRC 0x10 +#define ZIPCSIZ 0x14 +#define ZIPCUNC 0x18 +#define ZIPCFNL 0x1c +#define ZIPCXTL 0x1e +#define ZIPCCML 0x20 +#define ZIPDSK 0x22 +#define ZIPINT 0x24 +#define ZIPEXT 0x26 +#define ZIPOFST 0x2a +#define ZIPCFN 0x2e + +/* offsets in local file header structure */ +#define ZIPLOCSIG 0x00 +#define ZIPVER 0x04 +#define ZIPGENFLG 0x06 +#define ZIPMTHD 0x08 +#define ZIPTIME 0x0a +#define ZIPDATE 0x0c +#define ZIPCRC 0x0e +#define ZIPSIZE 0x12 +#define ZIPUNCMP 0x16 +#define ZIPFNLN 0x1a +#define ZIPXTRALN 0x1c +#define ZIPNAME 0x1e + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +INLINE UINT16 read_word(UINT8 *buf) +{ + return (buf[1] << 8) | buf[0]; +} + +INLINE UINT32 read_dword(UINT8 *buf) +{ + return (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; +} + + + +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +static zip_file *zip_cache[ZIP_CACHE_SIZE]; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +/* cache management */ +static void free_zip_file(zip_file *zip); + +/* ZIP file parsing */ +static zip_error read_ecd(zip_file *zip); +static zip_error get_compressed_data_offset(zip_file *zip, UINT64 *offset); + +/* decompression interfaces */ +static zip_error decompress_data_type_0(zip_file *zip, UINT64 offset, void *buffer, UINT32 length); +static zip_error decompress_data_type_8(zip_file *zip, UINT64 offset, void *buffer, UINT32 length); + + + +/*************************************************************************** + ZIP FILE ACCESS +***************************************************************************/ + +/*------------------------------------------------- + zip_file_open - opens a ZIP file for reading +-------------------------------------------------*/ + +zip_error zip_file_open(const char *filename, zip_file **zip) +{ + zip_error ziperr = ZIPERR_NONE; + file_error filerr; + UINT32 read_length; + zip_file *newzip; + char *string; + int cachenum; + + /* ensure we start with a NULL result */ + *zip = NULL; + + /* see if we are in the cache, and reopen if so */ + for (cachenum = 0; cachenum < ARRAY_LENGTH(zip_cache); cachenum++) + { + zip_file *cached = zip_cache[cachenum]; + + /* if we have a valid entry and it matches our filename, use it and remove from the cache */ + if (cached != NULL && cached->filename != NULL && strcmp(filename, cached->filename) == 0) + { + *zip = cached; + zip_cache[cachenum] = NULL; + return ZIPERR_NONE; + } + } + + /* allocate memory for the zip_file structure */ + newzip = malloc(sizeof(*newzip)); + if (newzip == NULL) + return ZIPERR_OUT_OF_MEMORY; + memset(newzip, 0, sizeof(*newzip)); + + /* open the file */ + filerr = osd_open(filename, OPEN_FLAG_READ, &newzip->file, &newzip->length); + if (filerr != FILERR_NONE) + { + ziperr = ZIPERR_FILE_ERROR; + goto error; + } + + /* read ecd data */ + ziperr = read_ecd(newzip); + if (ziperr != ZIPERR_NONE) + goto error; + + /* verify that we can work with this zipfile (no disk spanning allowed) */ + if (newzip->ecd.disk_number != newzip->ecd.cd_start_disk_number || newzip->ecd.cd_disk_entries != newzip->ecd.cd_total_entries) + { + ziperr = ZIPERR_UNSUPPORTED; + goto error; + } + + /* allocate memory for the central directory */ + newzip->cd = malloc(newzip->ecd.cd_size + 1); + if (newzip->cd == NULL) + { + ziperr = ZIPERR_OUT_OF_MEMORY; + goto error; + } + + /* read the central directory */ + filerr = osd_read(newzip->file, newzip->cd, newzip->ecd.cd_start_disk_offset, newzip->ecd.cd_size, &read_length); + if (filerr != FILERR_NONE || read_length != newzip->ecd.cd_size) + { + ziperr = (filerr == FILERR_NONE) ? ZIPERR_FILE_TRUNCATED : ZIPERR_FILE_ERROR; + goto error; + } + + /* make a copy of the filename for caching purposes */ + string = malloc(strlen(filename) + 1); + if (string == NULL) + { + ziperr = ZIPERR_OUT_OF_MEMORY; + goto error; + } + strcpy(string, filename); + newzip->filename = string; + *zip = newzip; + return ZIPERR_NONE; + +error: + free_zip_file(newzip); + return ziperr; +} + + +/*------------------------------------------------- + zip_file_close - close a ZIP file and add it + to the cache +-------------------------------------------------*/ + +void zip_file_close(zip_file *zip) +{ + int cachenum; + + /* close the open files */ + if (zip->file != NULL) + osd_close(zip->file); + zip->file = NULL; + + /* find the first NULL entry in the cache */ + for (cachenum = 0; cachenum < ARRAY_LENGTH(zip_cache); cachenum++) + if (zip_cache[cachenum] == NULL) + break; + + /* if no room left in the cache, free the bottommost entry */ + if (cachenum == ARRAY_LENGTH(zip_cache)) + free_zip_file(zip_cache[--cachenum]); + + /* move everyone else down and place us at the top */ + if (cachenum != 0) + memmove(&zip_cache[1], &zip_cache[0], cachenum * sizeof(zip_cache[0])); + zip_cache[0] = zip; +} + + +/*------------------------------------------------- + zip_file_cache_clear - clear the ZIP file + cache and free all memory +-------------------------------------------------*/ + +void zip_file_cache_clear(void) +{ + int cachenum; + + /* clear call cache entries */ + for (cachenum = 0; cachenum < ARRAY_LENGTH(zip_cache); cachenum++) + if (zip_cache[cachenum] != NULL) + { + free_zip_file(zip_cache[cachenum]); + zip_cache[cachenum] = NULL; + } +} + + + +/*************************************************************************** + CONTAINED FILE ACCESS +***************************************************************************/ + +/*------------------------------------------------- + zip_file_first_entry - return the first entry + in the ZIP +-------------------------------------------------*/ + +const zip_file_header *zip_file_first_file(zip_file *zip) +{ + /* reset the position and go from there */ + zip->cd_pos = 0; + return zip_file_next_file(zip); +} + + +/*------------------------------------------------- + zip_file_next_entry - return the next entry + in the ZIP +-------------------------------------------------*/ + +const zip_file_header *zip_file_next_file(zip_file *zip) +{ + /* fix up any modified data */ + if (zip->header.raw != NULL) + { + zip->header.raw[ZIPCFN + zip->header.filename_length] = zip->header.saved; + zip->header.raw = NULL; + } + + /* if we're at or past the end, we're done */ + if (zip->cd_pos >= zip->ecd.cd_size) + return NULL; + + /* extract file header info */ + zip->header.raw = zip->cd + zip->cd_pos; + zip->header.rawlength = ZIPCFN; + zip->header.signature = read_dword(zip->header.raw + ZIPCENSIG); + zip->header.version_created = read_word (zip->header.raw + ZIPCVER); + zip->header.version_needed = read_word (zip->header.raw + ZIPCVXT); + zip->header.bit_flag = read_word (zip->header.raw + ZIPCFLG); + zip->header.compression = read_word (zip->header.raw + ZIPCMTHD); + zip->header.file_time = read_word (zip->header.raw + ZIPCTIM); + zip->header.file_date = read_word (zip->header.raw + ZIPCDAT); + zip->header.crc = read_dword(zip->header.raw + ZIPCCRC); + zip->header.compressed_length = read_dword(zip->header.raw + ZIPCSIZ); + zip->header.uncompressed_length = read_dword(zip->header.raw + ZIPCUNC); + zip->header.filename_length = read_word (zip->header.raw + ZIPCFNL); + zip->header.extra_field_length = read_word (zip->header.raw + ZIPCXTL); + zip->header.file_comment_length = read_word (zip->header.raw + ZIPCCML); + zip->header.start_disk_number = read_word (zip->header.raw + ZIPDSK); + zip->header.internal_attributes = read_word (zip->header.raw + ZIPINT); + zip->header.external_attributes = read_dword(zip->header.raw + ZIPEXT); + zip->header.local_header_offset = read_dword(zip->header.raw + ZIPOFST); + zip->header.filename = (char *)zip->header.raw + ZIPCFN; + + /* make sure we have enough data */ + zip->header.rawlength += zip->header.filename_length; + zip->header.rawlength += zip->header.extra_field_length; + zip->header.rawlength += zip->header.file_comment_length; + if (zip->cd_pos + zip->header.rawlength > zip->ecd.cd_size) + return NULL; + + /* NULL terminate the filename */ + zip->header.saved = zip->header.raw[ZIPCFN + zip->header.filename_length]; + zip->header.raw[ZIPCFN + zip->header.filename_length] = 0; + + /* advance the position */ + zip->cd_pos += zip->header.rawlength; + return &zip->header; +} + + +/*------------------------------------------------- + zip_file_decompress - decompress a file + from a ZIP into the target buffer +-------------------------------------------------*/ + +zip_error zip_file_decompress(zip_file *zip, void *buffer, UINT32 length) +{ + zip_error ziperr; + UINT64 offset; + + /* if we don't have enough buffer, error */ + if (length < zip->header.uncompressed_length) + return ZIPERR_BUFFER_TOO_SMALL; + + /* make sure the info in the header aligns with what we know */ + if (zip->header.start_disk_number != zip->ecd.disk_number) + return ZIPERR_UNSUPPORTED; + + /* get the compressed data offset */ + ziperr = get_compressed_data_offset(zip, &offset); + if (ziperr != ZIPERR_NONE) + return ziperr; + + /* handle compression types */ + switch (zip->header.compression) + { + case 0: + ziperr = decompress_data_type_0(zip, offset, buffer, length); + break; + + case 8: + ziperr = decompress_data_type_8(zip, offset, buffer, length); + break; + + default: + ziperr = ZIPERR_UNSUPPORTED; + break; + } + return ziperr; +} + + + +/*************************************************************************** + CACHE MANAGEMENT +***************************************************************************/ + +/*------------------------------------------------- + free_zip_file - free all the data for a + zip_file +-------------------------------------------------*/ + +static void free_zip_file(zip_file *zip) +{ + if (zip != NULL) + { + if (zip->file != NULL) + osd_close(zip->file); + if (zip->filename != NULL) + free((void *)zip->filename); + if (zip->ecd.raw != NULL) + free(zip->ecd.raw); + if (zip->cd != NULL) + free(zip->cd); + free(zip); + } +} + + + +/*************************************************************************** + ZIP FILE PARSING +***************************************************************************/ + +/*------------------------------------------------- + read_ecd - read the ECD data +-------------------------------------------------*/ + +static zip_error read_ecd(zip_file *zip) +{ + UINT32 buflen = 1024; + UINT8 *buffer; + + /* we may need multiple tries */ + while (buflen < 65536) + { + file_error error; + UINT32 read_length; + INT32 offset; + + /* max out the buffer length at the size of the file */ + if (buflen > zip->length) + buflen = zip->length; + + /* allocate buffer */ + buffer = malloc(buflen + 1); + if (buffer == NULL) + return ZIPERR_OUT_OF_MEMORY; + + /* read in one buffers' worth of data */ + error = osd_read(zip->file, buffer, zip->length - buflen, buflen, &read_length); + if (error != FILERR_NONE || read_length != buflen) + { + free(buffer); + return ZIPERR_FILE_ERROR; + } + + /* find the ECD signature */ + for (offset = buflen - 22; offset >= 0; offset--) + if (buffer[offset + 0] == 'P' && buffer[offset + 1] == 'K' && buffer[offset + 2] == 0x05 && buffer[offset + 3] == 0x06) + break; + + /* if we found it, fill out the data */ + if (offset >= 0) + { + /* reuse the buffer as our ECD buffer */ + zip->ecd.raw = buffer; + zip->ecd.rawlength = buflen - offset; + + /* append a NULL terminator to the comment */ + memmove(&buffer[0], &buffer[offset], zip->ecd.rawlength); + zip->ecd.raw[zip->ecd.rawlength] = 0; + + /* extract ecd info */ + zip->ecd.signature = read_dword(zip->ecd.raw + ZIPESIG); + zip->ecd.disk_number = read_word (zip->ecd.raw + ZIPEDSK); + zip->ecd.cd_start_disk_number = read_word (zip->ecd.raw + ZIPECEN); + zip->ecd.cd_disk_entries = read_word (zip->ecd.raw + ZIPENUM); + zip->ecd.cd_total_entries = read_word (zip->ecd.raw + ZIPECENN); + zip->ecd.cd_size = read_dword(zip->ecd.raw + ZIPECSZ); + zip->ecd.cd_start_disk_offset = read_dword(zip->ecd.raw + ZIPEOFST); + zip->ecd.comment_length = read_word (zip->ecd.raw + ZIPECOML); + zip->ecd.comment = (const char *)(zip->ecd.raw + ZIPECOM); + return ZIPERR_NONE; + } + + /* didn't find it; free this buffer and expand our search */ + free(buffer); + if (buflen < zip->length) + buflen *= 2; + else + return ZIPERR_BAD_SIGNATURE; + } + return ZIPERR_OUT_OF_MEMORY; +} + + +/*------------------------------------------------- + get_compressed_data_offset - return the + offset of the compressed data +-------------------------------------------------*/ + +static zip_error get_compressed_data_offset(zip_file *zip, UINT64 *offset) +{ + file_error error; + UINT32 read_length; + + /* make sure the file handle is open */ + if (zip->file == NULL) + { + int filerr = osd_open(zip->filename, OPEN_FLAG_READ, &zip->file, &zip->length); + if (filerr != FILERR_NONE) + return ZIPERR_FILE_ERROR; + } + + /* now go read the fixed-sized part of the local file header */ + error = osd_read(zip->file, zip->buffer, zip->header.local_header_offset, ZIPNAME, &read_length); + if (error != FILERR_NONE || read_length != ZIPNAME) + return (error == FILERR_NONE) ? ZIPERR_FILE_TRUNCATED : ZIPERR_FILE_ERROR; + + /* compute the final offset */ + *offset = zip->header.local_header_offset + ZIPNAME; + *offset += read_word(zip->buffer + ZIPFNLN); + *offset += read_word(zip->buffer + ZIPXTRALN); + + return ZIPERR_NONE; +} + + + +/*************************************************************************** + DECOMPRESSION INTERFACES +***************************************************************************/ + +/*------------------------------------------------- + decompress_data_type_0 - "decompress" + type 0 data (which is uncompressed) +-------------------------------------------------*/ + +static zip_error decompress_data_type_0(zip_file *zip, UINT64 offset, void *buffer, UINT32 length) +{ + file_error filerr; + UINT32 read_length; + + /* the data is uncompressed; just read it */ + filerr = osd_read(zip->file, buffer, offset, zip->header.compressed_length, &read_length); + if (filerr != FILERR_NONE) + return ZIPERR_FILE_ERROR; + else if (read_length != zip->header.compressed_length) + return ZIPERR_FILE_TRUNCATED; + else + return ZIPERR_NONE; +} + + +/*------------------------------------------------- + decompress_data_type_8 - decompress + type 8 data (which is deflated) +-------------------------------------------------*/ + +static zip_error decompress_data_type_8(zip_file *zip, UINT64 offset, void *buffer, UINT32 length) +{ + UINT32 input_remaining = zip->header.compressed_length; + UINT32 read_length; + z_stream stream; + int filerr; + int zerr; + + /* make sure we don't need a newer mechanism */ + if (zip->header.version_needed > 0x14) + return ZIPERR_UNSUPPORTED; + + /* reset the stream */ + memset(&stream, 0, sizeof(stream)); + stream.next_out = buffer; + stream.avail_out = length; + + /* initialize the decompressor */ + zerr = inflateInit2(&stream, -MAX_WBITS); + if (zerr != Z_OK) + return ZIPERR_DECOMPRESS_ERROR; + + /* loop until we're done */ + while (1) + { + /* read in the next chunk of data */ + filerr = osd_read(zip->file, zip->buffer, offset, MIN(input_remaining, sizeof(zip->buffer)), &read_length); + if (filerr != FILERR_NONE) + { + inflateEnd(&stream); + return ZIPERR_FILE_ERROR; + } + offset += read_length; + + /* if we read nothing, but still have data left, the file is truncated */ + if (read_length == 0 && input_remaining > 0) + { + inflateEnd(&stream); + return ZIPERR_FILE_TRUNCATED; + } + + /* fill out the input data */ + stream.next_in = zip->buffer; + stream.avail_in = read_length; + input_remaining -= read_length; + + /* add a dummy byte at end of compressed data */ + if (input_remaining == 0) + stream.avail_in++; + + /* now inflate */ + zerr = inflate(&stream, Z_NO_FLUSH); + if (zerr == Z_STREAM_END) + break; + if (zerr != Z_OK) + { + inflateEnd(&stream); + return ZIPERR_DECOMPRESS_ERROR; + } + } + + /* finish decompression */ + zerr = inflateEnd(&stream); + if (zerr != Z_OK) + return ZIPERR_DECOMPRESS_ERROR; + + /* if anything looks funny, report an error */ + if (stream.avail_out > 0 || input_remaining > 0) + return ZIPERR_DECOMPRESS_ERROR; + + return ZIPERR_NONE; +} diff --git a/src/lib/util/unzip.h b/src/lib/util/unzip.h new file mode 100644 index 00000000000..0cc9842994d --- /dev/null +++ b/src/lib/util/unzip.h @@ -0,0 +1,143 @@ +/*************************************************************************** + + unzip.h + + ZIP file management. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#pragma once + +#ifndef __UNZIP_H__ +#define __UNZIP_H__ + +#include "osdcore.h" + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define ZIP_DECOMPRESS_BUFSIZE 16384 + +/* Error types */ +enum _zip_error +{ + ZIPERR_NONE = 0, + ZIPERR_OUT_OF_MEMORY, + ZIPERR_FILE_ERROR, + ZIPERR_BAD_SIGNATURE, + ZIPERR_DECOMPRESS_ERROR, + ZIPERR_FILE_TRUNCATED, + ZIPERR_FILE_CORRUPT, + ZIPERR_UNSUPPORTED, + ZIPERR_BUFFER_TOO_SMALL +}; +typedef enum _zip_error zip_error; + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* contains extracted file header information */ +typedef struct _zip_file_header zip_file_header; +struct _zip_file_header +{ + UINT32 signature; /* central file header signature */ + UINT16 version_created; /* version made by */ + UINT16 version_needed; /* version needed to extract */ + UINT16 bit_flag; /* general purpose bit flag */ + UINT16 compression; /* compression method */ + UINT16 file_time; /* last mod file time */ + UINT16 file_date; /* last mod file date */ + UINT32 crc; /* crc-32 */ + UINT32 compressed_length; /* compressed size */ + UINT32 uncompressed_length; /* uncompressed size */ + UINT16 filename_length; /* filename length */ + UINT16 extra_field_length; /* extra field length */ + UINT16 file_comment_length; /* file comment length */ + UINT16 start_disk_number; /* disk number start */ + UINT16 internal_attributes; /* internal file attributes */ + UINT32 external_attributes; /* external file attributes */ + UINT32 local_header_offset; /* relative offset of local header */ + const char * filename; /* filename */ + + UINT8 * raw; /* pointer to the raw data */ + UINT32 rawlength; /* length of the raw data */ + UINT8 saved; /* saved byte from after filename */ +}; + + +/* contains extracted end of central directory information */ +typedef struct _zip_ecd zip_ecd; +struct _zip_ecd +{ + UINT32 signature; /* end of central dir signature */ + UINT16 disk_number; /* number of this disk */ + UINT16 cd_start_disk_number; /* number of the disk with the start of the central directory */ + UINT16 cd_disk_entries; /* total number of entries in the central directory on this disk */ + UINT16 cd_total_entries; /* total number of entries in the central directory */ + UINT32 cd_size; /* size of the central directory */ + UINT32 cd_start_disk_offset; /* offset of start of central directory with respect to the starting disk number */ + UINT16 comment_length; /* .ZIP file comment length */ + const char * comment; /* .ZIP file comment */ + + UINT8 * raw; /* pointer to the raw data */ + UINT32 rawlength; /* length of the raw data */ +}; + + +/* describes an open ZIP file */ +typedef struct _zip_file zip_file; +struct _zip_file +{ + const char * filename; /* copy of ZIP filename (for caching) */ + osd_file * file; /* OSD file handle */ + UINT64 length; /* length of zip file */ + + zip_ecd ecd; /* end of central directory */ + + UINT8 * cd; /* central directory raw data */ + UINT32 cd_pos; /* position in central directory */ + zip_file_header header; /* current file header */ + + UINT8 buffer[ZIP_DECOMPRESS_BUFSIZE]; /* buffer for decompression */ +}; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + + +/* ----- ZIP file access ----- */ + +/* open a ZIP file and parse its central directory */ +zip_error zip_file_open(const char *filename, zip_file **zip); + +/* close a ZIP file (may actually be left open due to caching) */ +void zip_file_close(zip_file *zip); + +/* clear out all open ZIP files from the cache */ +void zip_file_cache_clear(void); + + +/* ----- contained file access ----- */ + +/* find the first file in the ZIP */ +const zip_file_header *zip_file_first_file(zip_file *zip); + +/* find the next file in the ZIP */ +const zip_file_header *zip_file_next_file(zip_file *zip); + +/* decompress the most recently found file in the ZIP */ +zip_error zip_file_decompress(zip_file *zip, void *buffer, UINT32 length); + + +#endif /* __UNZIP_H__ */ diff --git a/src/lib/util/xmlfile.c b/src/lib/util/xmlfile.c new file mode 100644 index 00000000000..a220cc05e4a --- /dev/null +++ b/src/lib/util/xmlfile.c @@ -0,0 +1,857 @@ +/*************************************************************************** + + xmlfile.c + + XML file parsing code. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#include "xmlfile.h" +#include <ctype.h> +#include <expat.h> + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define TEMP_BUFFER_SIZE 4096 + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +typedef struct _xml_parse_info xml_parse_info; +struct _xml_parse_info +{ + XML_Parser parser; + xml_data_node * rootnode; + xml_data_node * curnode; + UINT32 flags; +}; + + + +/*************************************************************************** + PROTOTYPES +***************************************************************************/ + +/* expat interfaces */ +static int expat_setup_parser(xml_parse_info *parse_info, xml_parse_options *opts); +static void expat_element_start(void *data, const XML_Char *name, const XML_Char **attributes); +static void expat_data(void *data, const XML_Char *s, int len); +static void expat_element_end(void *data, const XML_Char *name); + +/* node/attributes additions */ +static xml_data_node *add_child(xml_data_node *parent, const char *name, const char *value); +static xml_attribute_node *add_attribute(xml_data_node *node, const char *name, const char *value); + +/* recursive tree operations */ +static void write_node_recursive(xml_data_node *node, int indent, core_file *file); +static void free_node_recursive(xml_data_node *node); + + + +/*************************************************************************** + CORE IMPLEMENTATION +***************************************************************************/ + +/*------------------------------------------------- + copystring - make an allocated copy of a + string +-------------------------------------------------*/ + +static const char *copystring(const char *input) +{ + char *newstr; + + /* NULL just passes through */ + if (input == NULL) + return NULL; + + /* make a lower-case copy if the allocation worked */ + newstr = malloc(strlen(input) + 1); + if (newstr != NULL) + strcpy(newstr, input); + + return newstr; +} + + +/*------------------------------------------------- + copystring_lower - make an allocated copy of + a string and convert it to lowercase along + the way +-------------------------------------------------*/ + +static const char *copystring_lower(const char *input) +{ + char *newstr; + int i; + + /* NULL just passes through */ + if (input == NULL) + return NULL; + + /* make a lower-case copy if the allocation worked */ + newstr = malloc(strlen(input) + 1); + if (newstr != NULL) + { + for (i = 0; input[i] != 0; i++) + newstr[i] = tolower(input[i]); + newstr[i] = 0; + } + + return newstr; +} + + + +/*************************************************************************** + XML FILE OBJECTS +***************************************************************************/ + +/*------------------------------------------------- + xml_file_create - create a new xml file + object +-------------------------------------------------*/ + +xml_data_node *xml_file_create(void) +{ + xml_data_node *rootnode; + + /* create a root node */ + rootnode = malloc(sizeof(*rootnode)); + if (rootnode == NULL) + return NULL; + memset(rootnode, 0, sizeof(*rootnode)); + return rootnode; +} + + +/*------------------------------------------------- + xml_file_read - parse an XML file into its + nodes +-------------------------------------------------*/ + +xml_data_node *xml_file_read(core_file *file, xml_parse_options *opts) +{ + xml_parse_info parse_info; + int done; + + /* set up the parser */ + if (!expat_setup_parser(&parse_info, opts)) + return NULL; + + /* loop through the file and parse it */ + do + { + char tempbuf[TEMP_BUFFER_SIZE]; + + /* read as much as we can */ + int bytes = core_fread(file, tempbuf, sizeof(tempbuf)); + done = core_feof(file); + + /* parse the data */ + if (XML_Parse(parse_info.parser, tempbuf, bytes, done) == XML_STATUS_ERROR) + { + if (opts != NULL && opts->error != NULL) + { + opts->error->error_message = XML_ErrorString(XML_GetErrorCode(parse_info.parser)); + opts->error->error_line = XML_GetCurrentLineNumber(parse_info.parser); + opts->error->error_column = XML_GetCurrentColumnNumber(parse_info.parser); + } + + xml_file_free(parse_info.rootnode); + XML_ParserFree(parse_info.parser); + return NULL; + } + + } while (!done); + + /* free the parser */ + XML_ParserFree(parse_info.parser); + + /* return the root node */ + return parse_info.rootnode; +} + + +/*------------------------------------------------- + xml_string_read - parse an XML string into its + nodes +-------------------------------------------------*/ + +xml_data_node *xml_string_read(const char *string, xml_parse_options *opts) +{ + xml_parse_info parse_info; + int length = (int)strlen(string); + + /* set up the parser */ + if (!expat_setup_parser(&parse_info, opts)) + return NULL; + + /* parse the data */ + if (XML_Parse(parse_info.parser, string, length, TRUE) == XML_STATUS_ERROR) + { + if (opts != NULL && opts->error != NULL) + { + opts->error->error_message = XML_ErrorString(XML_GetErrorCode(parse_info.parser)); + opts->error->error_line = XML_GetCurrentLineNumber(parse_info.parser); + opts->error->error_column = XML_GetCurrentColumnNumber(parse_info.parser); + } + + xml_file_free(parse_info.rootnode); + XML_ParserFree(parse_info.parser); + return NULL; + } + + /* free the parser */ + XML_ParserFree(parse_info.parser); + + /* return the root node */ + return parse_info.rootnode; +} + + +/*------------------------------------------------- + xml_file_write - write an XML tree to a file +-------------------------------------------------*/ + +void xml_file_write(xml_data_node *node, core_file *file) +{ + /* ensure this is a root node */ + if (node->name != NULL) + return; + + /* output a simple header */ + core_fprintf(file, "<?xml version=\"1.0\"?>\n"); + core_fprintf(file, "<!-- This file is autogenerated; comments and unknown tags will be stripped -->\n"); + + /* loop over children of the root and output */ + for (node = node->child; node; node = node->next) + write_node_recursive(node, 0, file); +} + + +/*------------------------------------------------- + xml_file_free - free an XML file object +-------------------------------------------------*/ + +void xml_file_free(xml_data_node *node) +{ + /* ensure this is a root node */ + if (node->name != NULL) + return; + + free_node_recursive(node); +} + + + +/*************************************************************************** + XML NODE MANAGEMENT +***************************************************************************/ + +/*------------------------------------------------- + xml_count_children - count the number of + child nodes +-------------------------------------------------*/ + +int xml_count_children(xml_data_node *node) +{ + int count = 0; + + /* loop over children and count */ + for (node = node->child; node; node = node->next) + count++; + return count; +} + + +/*------------------------------------------------- + xml_get_sibling - find the next sibling of + the specified node with the specified tag +-------------------------------------------------*/ + +xml_data_node *xml_get_sibling(xml_data_node *node, const char *name) +{ + /* loop over siblings and find a matching name */ + for ( ; node; node = node->next) + if (strcmp(node->name, name) == 0) + return node; + return NULL; +} + + +/*------------------------------------------------- + xml_find_matching_sibling - find the next + sibling of the specified node with the + specified tag or attribute/value pair +-------------------------------------------------*/ + +xml_data_node *xml_find_matching_sibling(xml_data_node *node, const char *name, const char *attribute, const char *matchval) +{ + /* loop over siblings and find a matching attribute */ + for ( ; node; node = node->next) + { + /* can pass NULL as a wildcard for the node name */ + if (name == NULL || strcmp(name, node->name) == 0) + { + /* find a matching attribute */ + xml_attribute_node *attr = xml_get_attribute(node, attribute); + if (attr != NULL && strcmp(attr->value, matchval) == 0) + return node; + } + } + return NULL; +} + + +/*------------------------------------------------- + xml_add_child - add a new child node to the + given node +-------------------------------------------------*/ + +xml_data_node *xml_add_child(xml_data_node *node, const char *name, const char *value) +{ + /* just a standard add child */ + return add_child(node, name, value); +} + + +/*------------------------------------------------- + xml_get_or_add_child - find a child node of + the specified type; if not found, add one +-------------------------------------------------*/ + +xml_data_node *xml_get_or_add_child(xml_data_node *node, const char *name, const char *value) +{ + xml_data_node *child; + + /* find the child first */ + child = xml_get_sibling(node->child, name); + if (child != NULL) + return child; + + /* if not found, do a standard add child */ + return add_child(node, name, value); +} + + +/*------------------------------------------------- + xml_delete_node - delete a node and its + children +-------------------------------------------------*/ + +void xml_delete_node(xml_data_node *node) +{ + xml_data_node **pnode; + + /* first unhook us from the list of children of our parent */ + for (pnode = &node->parent->child; *pnode; pnode = &(*pnode)->next) + if (*pnode == node) + { + *pnode = node->next; + break; + } + + /* now free ourselves and our children */ + free_node_recursive(node); +} + + + +/*************************************************************************** + XML ATTRIBUTE MANAGEMENT +***************************************************************************/ + +/*------------------------------------------------- + xml_get_attribute - get the value of the + specified attribute, or NULL if not found +-------------------------------------------------*/ + +xml_attribute_node *xml_get_attribute(xml_data_node *node, const char *attribute) +{ + xml_attribute_node *anode; + + /* loop over attributes and find a match */ + for (anode = node->attribute; anode; anode = anode->next) + if (strcmp(anode->name, attribute) == 0) + return anode; + return NULL; +} + + +/*------------------------------------------------- + xml_get_attribute_string - get the string + value of the specified attribute; if not + found, return = the provided default +-------------------------------------------------*/ + +const char *xml_get_attribute_string(xml_data_node *node, const char *attribute, const char *defvalue) +{ + xml_attribute_node *attr = xml_get_attribute(node, attribute); + return attr ? attr->value : defvalue; +} + + +/*------------------------------------------------- + xml_get_attribute_int - get the integer + value of the specified attribute; if not + found, return = the provided default +-------------------------------------------------*/ + +int xml_get_attribute_int(xml_data_node *node, const char *attribute, int defvalue) +{ + const char *string = xml_get_attribute_string(node, attribute, NULL); + int value; + + if (string == NULL || sscanf(string, "%d", &value) != 1) + return defvalue; + return value; +} + + +/*------------------------------------------------- + xml_get_attribute_float - get the float + value of the specified attribute; if not + found, return = the provided default +-------------------------------------------------*/ + +float xml_get_attribute_float(xml_data_node *node, const char *attribute, float defvalue) +{ + const char *string = xml_get_attribute_string(node, attribute, NULL); + float value; + + if (string == NULL || sscanf(string, "%f", &value) != 1) + return defvalue; + return value; +} + + +/*------------------------------------------------- + xml_set_attribute - set a new attribute and + string value on the node +-------------------------------------------------*/ + +xml_attribute_node *xml_set_attribute(xml_data_node *node, const char *name, const char *value) +{ + xml_attribute_node *anode; + + /* first find an existing one to replace */ + anode = xml_get_attribute(node, name); + + /* if we found it, free the old value and replace it */ + if (anode != NULL) + { + if (anode->value != NULL) + free((void *)anode->value); + anode->value = copystring(value); + } + + /* otherwise, create a new node */ + else + anode = add_attribute(node, name, value); + + return anode; +} + + +/*------------------------------------------------- + xml_set_attribute_int - set a new attribute and + integer value on the node +-------------------------------------------------*/ + +xml_attribute_node *xml_set_attribute_int(xml_data_node *node, const char *name, int value) +{ + char buffer[100]; + sprintf(buffer, "%d", value); + return xml_set_attribute(node, name, buffer); +} + + +/*------------------------------------------------- + xml_set_attribute_int - set a new attribute and + float value on the node +-------------------------------------------------*/ + +xml_attribute_node *xml_set_attribute_float(xml_data_node *node, const char *name, float value) +{ + char buffer[100]; + sprintf(buffer, "%f", value); + return xml_set_attribute(node, name, buffer); +} + + + +/*************************************************************************** + MISCELLANEOUS INTERFACES +***************************************************************************/ + +/*------------------------------------------------- + xml_normalize_string - normalize a string + to ensure it doesn't contain embedded tags +-------------------------------------------------*/ + +const char *xml_normalize_string(const char *string) +{ + static char buffer[1024]; + char *d = &buffer[0]; + + if (string != NULL) + { + while (*string) + { + switch (*string) + { + case '\"' : d += sprintf(d, """); break; + case '&' : d += sprintf(d, "&"); break; + case '<' : d += sprintf(d, "<"); break; + case '>' : d += sprintf(d, ">"); break; + default: + if (*string >= ' ' && *string <= '~') + *d++ = *string; + else + d += sprintf(d, "&#%d;", (unsigned)(unsigned char)*string); + } + ++string; + } + } + *d++ = 0; + return buffer; +} + + + +/*************************************************************************** + EXPAT INTERFACES +***************************************************************************/ + +/*------------------------------------------------- + expat_setup_parser - set up expat for parsing +-------------------------------------------------*/ + +static int expat_setup_parser(xml_parse_info *parse_info, xml_parse_options *opts) +{ + /* setup parse_info structure */ + memset(parse_info, 0, sizeof(*parse_info)); + if (opts != NULL) + { + parse_info->flags = opts->flags; + if (opts->error != NULL) + { + opts->error->error_message = NULL; + opts->error->error_line = 0; + opts->error->error_column = 0; + } + } + + /* create a root node */ + parse_info->rootnode = xml_file_create(); + if (parse_info->rootnode == NULL) + return FALSE; + parse_info->curnode = parse_info->rootnode; + + /* create the XML parser */ + parse_info->parser = XML_ParserCreate(NULL); + if (parse_info->parser == NULL) + { + free(parse_info->rootnode); + return FALSE; + } + + /* configure the parser */ + XML_SetElementHandler(parse_info->parser, expat_element_start, expat_element_end); + XML_SetCharacterDataHandler(parse_info->parser, expat_data); + XML_SetUserData(parse_info->parser, parse_info); + + /* optional parser initialization step */ + if (opts != NULL && opts->init_parser != NULL) + (*opts->init_parser)(parse_info->parser); + return TRUE; +} + + +/*------------------------------------------------- + expat_element_start - expat callback for a new + element +-------------------------------------------------*/ + +static void expat_element_start(void *data, const XML_Char *name, const XML_Char **attributes) +{ + xml_parse_info *parse_info = (xml_parse_info *) data; + xml_data_node **curnode = &parse_info->curnode; + xml_data_node *newnode; + int attr; + + /* add a new child node to the current node */ + newnode = add_child(*curnode, name, NULL); + if (newnode == NULL) + return; + + /* add all the attributes as well */ + for (attr = 0; attributes[attr]; attr += 2) + add_attribute(newnode, attributes[attr+0], attributes[attr+1]); + + /* set us up as the current node */ + *curnode = newnode; +} + + +/*------------------------------------------------- + expat_data - expat callback for a additional + element data +-------------------------------------------------*/ + +static void expat_data(void *data, const XML_Char *s, int len) +{ + xml_parse_info *parse_info = (xml_parse_info *) data; + xml_data_node **curnode = &parse_info->curnode; + int oldlen = 0; + char *newdata; + + /* if no data, skip */ + if (len == 0) + return; + + /* determine how much data we currently have */ + if ((*curnode)->value != NULL) + oldlen = (int)strlen((*curnode)->value); + + /* realloc */ + newdata = realloc((void *)(*curnode)->value, oldlen + len + 1); + if (newdata == NULL) + return; + + /* copy in the new data a NULL-terminate */ + memcpy(&newdata[oldlen], s, len); + newdata[oldlen + len] = 0; + (*curnode)->value = newdata; +} + + +/*------------------------------------------------- + expat_element_end - expat callback for the end + of an element +-------------------------------------------------*/ + +static void expat_element_end(void *data, const XML_Char *name) +{ + xml_parse_info *parse_info = (xml_parse_info *) data; + xml_data_node **curnode = &parse_info->curnode; + char *orig; + + /* strip leading/trailing spaces from the value data */ + orig = (char *)(*curnode)->value; + if (orig != NULL && !(parse_info->flags & XML_PARSE_FLAG_WHITESPACE_SIGNIFICANT)) + { + char *start = orig; + char *end = start + strlen(start); + + /* first strip leading spaces */ + while (*start && isspace(*start)) + start++; + + /* then strip trailing spaces */ + while (end > start && isspace(end[-1])) + end--; + + /* if nothing left, just free it */ + if (start == end) + { + free(orig); + (*curnode)->value = NULL; + } + + /* otherwise, memmove the data */ + else + { + memmove(orig, start, end - start); + orig[end - start] = 0; + } + } + + /* back us up a node */ + *curnode = (*curnode)->parent; +} + + + +/*************************************************************************** + NODE/ATTRIBUTE ADDITIONS +***************************************************************************/ + +/*------------------------------------------------- + add_child - add a new node to the parent +-------------------------------------------------*/ + +static xml_data_node *add_child(xml_data_node *parent, const char *name, const char *value) +{ + xml_data_node **pnode; + xml_data_node *node; + + /* new element: create a new node */ + node = malloc(sizeof(*node)); + if (node == NULL) + return NULL; + + /* initialize the members */ + node->next = NULL; + node->parent = parent; + node->child = NULL; + node->name = copystring_lower(name); + if (node->name == NULL) + { + free(node); + return NULL; + } + node->value = copystring(value); + if (node->value == NULL && value != NULL) + { + free((void *)node->name); + free(node); + return NULL; + } + node->attribute = NULL; + + /* add us to the end of the list of siblings */ + for (pnode = &parent->child; *pnode; pnode = &(*pnode)->next) ; + *pnode = node; + + return node; +} + + +/*------------------------------------------------- + add_attribute - add a new attribute to the + given node +-------------------------------------------------*/ + +static xml_attribute_node *add_attribute(xml_data_node *node, const char *name, const char *value) +{ + xml_attribute_node *anode, **panode; + + /* allocate a new attribute node */ + anode = malloc(sizeof(*anode)); + if (anode == NULL) + return NULL; + + /* fill it in */ + anode->next = NULL; + anode->name = copystring_lower(name); + if (anode->name == NULL) + { + free(anode); + return NULL; + } + anode->value = copystring(value); + if (anode->value == NULL) + { + free((void *)anode->name); + free(anode); + return NULL; + } + + /* add us to the end of the list of attributes */ + for (panode = &node->attribute; *panode; panode = &(*panode)->next) ; + *panode = anode; + + return anode; +} + + + +/*************************************************************************** + RECURSIVE TREE OPERATIONS +***************************************************************************/ + +/*------------------------------------------------- + write_node_recursive - recursively write + an XML node and its children to a file +-------------------------------------------------*/ + +static void write_node_recursive(xml_data_node *node, int indent, core_file *file) +{ + xml_attribute_node *anode; + xml_data_node *child; + + /* output this tag */ + core_fprintf(file, "%*s<%s", indent, "", node->name); + + /* output any attributes */ + for (anode = node->attribute; anode; anode = anode->next) + core_fprintf(file, " %s=\"%s\"", anode->name, anode->value); + + /* if there are no children and no value, end the tag here */ + if (node->child == NULL && node->value == NULL) + core_fprintf(file, " />\n"); + + /* otherwise, close this tag and output more stuff */ + else + { + core_fprintf(file, ">\n"); + + /* if there is a value, output that here */ + if (node->value != NULL) + core_fprintf(file, "%*s%s\n", indent + 4, "", node->value); + + /* loop over children and output them as well */ + if (node->child != NULL) + { + for (child = node->child; child; child = child->next) + write_node_recursive(child, indent + 4, file); + } + + /* write a closing tag */ + core_fprintf(file, "%*s</%s>\n", indent, "", node->name); + } +} + + +/*------------------------------------------------- + free_node_recursive - recursively free + the data allocated to an XML node +-------------------------------------------------*/ + +static void free_node_recursive(xml_data_node *node) +{ + xml_attribute_node *anode, *nanode; + xml_data_node *child, *nchild; + + /* free name/value */ + if (node->name != NULL) + free((void *)node->name); + if (node->value != NULL) + free((void *)node->value); + + /* free attributes */ + for (anode = node->attribute; anode; anode = nanode) + { + /* free name/value */ + if (anode->name != NULL) + free((void *)anode->name); + if (anode->value != NULL) + free((void *)anode->value); + + /* note the next node and free this node */ + nanode = anode->next; + free(anode); + } + + /* free the children */ + for (child = node->child; child; child = nchild) + { + /* note the next node and free this node */ + nchild = child->next; + free_node_recursive(child); + } + + /* finally free ourself */ + free(node); +} diff --git a/src/lib/util/xmlfile.h b/src/lib/util/xmlfile.h new file mode 100644 index 00000000000..f68c81fddb2 --- /dev/null +++ b/src/lib/util/xmlfile.h @@ -0,0 +1,160 @@ +/*************************************************************************** + + xmlfile.h + + XML file parsing code. + + Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. + Visit http://mamedev.org for licensing and usage restrictions. + +***************************************************************************/ + +#pragma once + +#ifndef __XMLFILE_H__ +#define __XMLFILE_H__ + +#include "osdcore.h" +#include "corefile.h" + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +enum +{ + XML_PARSE_FLAG_WHITESPACE_SIGNIFICANT = 1 +}; + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* forward type declarations */ +struct XML_ParserStruct; + + +/* a node representing an attribute */ +typedef struct _xml_attribute_node xml_attribute_node; +struct _xml_attribute_node +{ + xml_attribute_node * next; /* pointer to next attribute node */ + const char * name; /* pointer to copy of tag name */ + const char * value; /* pointer to copy of value string */ +}; + + +/* a node representing a data item and its relationships */ +typedef struct _xml_data_node xml_data_node; +struct _xml_data_node +{ + xml_data_node * next; /* pointer to next sibling node */ + xml_data_node * parent; /* pointer to parent node */ + xml_data_node * child; /* pointer to first child node */ + const char * name; /* pointer to copy of tag name */ + const char * value; /* pointer to copy of value string */ + xml_attribute_node * attribute; /* pointer to array of attribute nodes */ +}; + + +/* extended error information from parsing */ +typedef struct _xml_parse_error xml_parse_error; +struct _xml_parse_error +{ + const char * error_message; + int error_line; + int error_column; +}; + + +/* parsing options */ +typedef struct _xml_parse_options xml_parse_options; +struct _xml_parse_options +{ + xml_parse_error * error; + void (*init_parser)(struct XML_ParserStruct *parser); + UINT32 flags; +}; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + + +/* ----- XML file objects ----- */ + +/* create a new empty xml file object */ +xml_data_node *xml_file_create(void); + +/* parse an XML file into its nodes */ +xml_data_node *xml_file_read(core_file *file, xml_parse_options *opts); + +/* parse an XML string into its nodes */ +xml_data_node *xml_string_read(const char *string, xml_parse_options *opts); + +/* write an XML tree to a file */ +void xml_file_write(xml_data_node *node, core_file *file); + +/* free an XML file object */ +void xml_file_free(xml_data_node *node); + + + +/* ----- XML node management ----- */ + +/* count the number of child nodes */ +int xml_count_children(xml_data_node *node); + +/* find the next sibling with the given tag */ +xml_data_node *xml_get_sibling(xml_data_node *node, const char *name); + +/* find the next sibling with the given tag and/or attribute/value pair */ +xml_data_node *xml_find_matching_sibling(xml_data_node *node, const char *name, const char *attribute, const char *matchval); + +/* add a new child node */ +xml_data_node *xml_add_child(xml_data_node *node, const char *name, const char *value); + +/* either return an existing child node or create one if it doesn't exist */ +xml_data_node *xml_get_or_add_child(xml_data_node *node, const char *name, const char *value); + +/* delete a node and its children */ +void xml_delete_node(xml_data_node *node); + + + +/* ----- XML attribute management ----- */ + +/* find an attribute node with the specified tag */ +xml_attribute_node *xml_get_attribute(xml_data_node *node, const char *attribute); + +/* return the string value of an attribute, or the specified default if not present */ +const char *xml_get_attribute_string(xml_data_node *node, const char *attribute, const char *defvalue); + +/* return the integer value of an attribute, or the specified default if not present */ +int xml_get_attribute_int(xml_data_node *node, const char *attribute, int defvalue); + +/* return the float value of an attribute, or the specified default if not present */ +float xml_get_attribute_float(xml_data_node *node, const char *attribute, float defvalue); + +/* set the string value of an attribute */ +xml_attribute_node *xml_set_attribute(xml_data_node *node, const char *name, const char *value); + +/* set the integer value of an attribute */ +xml_attribute_node *xml_set_attribute_int(xml_data_node *node, const char *name, int value); + +/* set the float value of an attribute */ +xml_attribute_node *xml_set_attribute_float(xml_data_node *node, const char *name, float value); + + + +/* ----- miscellaneous interfaces ----- */ + +/* normalize a string into something that can be written to an XML file */ +const char *xml_normalize_string(const char *string); + +#endif /* __XMLFILE_H__ */ diff --git a/src/lib/zlib/Makefile b/src/lib/zlib/Makefile new file mode 100644 index 00000000000..2fd6e45c48d --- /dev/null +++ b/src/lib/zlib/Makefile @@ -0,0 +1,154 @@ +# Makefile for zlib +# Copyright (C) 1995-2005 Jean-loup Gailly. +# For conditions of distribution and use, see copyright notice in zlib.h + +# To compile and test, type: +# ./configure; make test +# The call of configure is optional if you don't have special requirements +# If you wish to build zlib as a shared library, use: ./configure -s + +# To use the asm code, type: +# cp contrib/asm?86/match.S ./match.S +# make LOC=-DASMV OBJA=match.o + +# To install /usr/local/lib/libz.* and /usr/local/include/zlib.h, type: +# make install +# To install in $HOME instead of /usr/local, use: +# make install prefix=$HOME + +CC=cc + +CFLAGS=-O +#CFLAGS=-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7 +#CFLAGS=-g -DDEBUG +#CFLAGS=-O3 -Wall -Wwrite-strings -Wpointer-arith -Wconversion \ +# -Wstrict-prototypes -Wmissing-prototypes + +LDFLAGS=libz.a +LDSHARED=$(CC) +CPP=$(CC) -E + +LIBS=libz.a +SHAREDLIB=libz.so +SHAREDLIBV=libz.so.1.2.3 +SHAREDLIBM=libz.so.1 + +AR=ar rc +RANLIB=ranlib +TAR=tar +SHELL=/bin/sh +EXE= + +prefix = /usr/local +exec_prefix = ${prefix} +libdir = ${exec_prefix}/lib +includedir = ${prefix}/include +mandir = ${prefix}/share/man +man3dir = ${mandir}/man3 + +OBJS = adler32.o compress.o crc32.o gzio.o uncompr.o deflate.o trees.o \ + zutil.o inflate.o infback.o inftrees.o inffast.o + +OBJA = +# to use the asm code: make OBJA=match.o + +TEST_OBJS = example.o minigzip.o + +all: example$(EXE) minigzip$(EXE) + +check: test +test: all + @LD_LIBRARY_PATH=.:$(LD_LIBRARY_PATH) ; export LD_LIBRARY_PATH; \ + echo hello world | ./minigzip | ./minigzip -d || \ + echo ' *** minigzip test FAILED ***' ; \ + if ./example; then \ + echo ' *** zlib test OK ***'; \ + else \ + echo ' *** zlib test FAILED ***'; \ + fi + +libz.a: $(OBJS) $(OBJA) + $(AR) $@ $(OBJS) $(OBJA) + -@ ($(RANLIB) $@ || true) >/dev/null 2>&1 + +match.o: match.S + $(CPP) match.S > _match.s + $(CC) -c _match.s + mv _match.o match.o + rm -f _match.s + +$(SHAREDLIBV): $(OBJS) + $(LDSHARED) -o $@ $(OBJS) + rm -f $(SHAREDLIB) $(SHAREDLIBM) + ln -s $@ $(SHAREDLIB) + ln -s $@ $(SHAREDLIBM) + +example$(EXE): example.o $(LIBS) + $(CC) $(CFLAGS) -o $@ example.o $(LDFLAGS) + +minigzip$(EXE): minigzip.o $(LIBS) + $(CC) $(CFLAGS) -o $@ minigzip.o $(LDFLAGS) + +install: $(LIBS) + -@if [ ! -d $(exec_prefix) ]; then mkdir -p $(exec_prefix); fi + -@if [ ! -d $(includedir) ]; then mkdir -p $(includedir); fi + -@if [ ! -d $(libdir) ]; then mkdir -p $(libdir); fi + -@if [ ! -d $(man3dir) ]; then mkdir -p $(man3dir); fi + cp zlib.h zconf.h $(includedir) + chmod 644 $(includedir)/zlib.h $(includedir)/zconf.h + cp $(LIBS) $(libdir) + cd $(libdir); chmod 755 $(LIBS) + -@(cd $(libdir); $(RANLIB) libz.a || true) >/dev/null 2>&1 + cd $(libdir); if test -f $(SHAREDLIBV); then \ + rm -f $(SHAREDLIB) $(SHAREDLIBM); \ + ln -s $(SHAREDLIBV) $(SHAREDLIB); \ + ln -s $(SHAREDLIBV) $(SHAREDLIBM); \ + (ldconfig || true) >/dev/null 2>&1; \ + fi + cp zlib.3 $(man3dir) + chmod 644 $(man3dir)/zlib.3 +# The ranlib in install is needed on NeXTSTEP which checks file times +# ldconfig is for Linux + +uninstall: + cd $(includedir); \ + cd $(libdir); rm -f libz.a; \ + if test -f $(SHAREDLIBV); then \ + rm -f $(SHAREDLIBV) $(SHAREDLIB) $(SHAREDLIBM); \ + fi + cd $(man3dir); rm -f zlib.3 + +mostlyclean: clean +clean: + rm -f *.o *~ example$(EXE) minigzip$(EXE) \ + libz.* foo.gz so_locations \ + _match.s maketree contrib/infback9/*.o + +maintainer-clean: distclean +distclean: clean + cp -p Makefile.in Makefile + cp -p zconf.in.h zconf.h + rm -f .DS_Store + +tags: + etags *.[ch] + +depend: + makedepend -- $(CFLAGS) -- *.[ch] + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +adler32.o: zlib.h zconf.h +compress.o: zlib.h zconf.h +crc32.o: crc32.h zlib.h zconf.h +deflate.o: deflate.h zutil.h zlib.h zconf.h +example.o: zlib.h zconf.h +gzio.o: zutil.h zlib.h zconf.h +inffast.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h +inflate.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h +infback.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h +inftrees.o: zutil.h zlib.h zconf.h inftrees.h +minigzip.o: zlib.h zconf.h +trees.o: deflate.h zutil.h zlib.h zconf.h trees.h +uncompr.o: zlib.h zconf.h +zutil.o: zutil.h zlib.h zconf.h diff --git a/src/lib/zlib/Makefile.in b/src/lib/zlib/Makefile.in new file mode 100644 index 00000000000..2fd6e45c48d --- /dev/null +++ b/src/lib/zlib/Makefile.in @@ -0,0 +1,154 @@ +# Makefile for zlib +# Copyright (C) 1995-2005 Jean-loup Gailly. +# For conditions of distribution and use, see copyright notice in zlib.h + +# To compile and test, type: +# ./configure; make test +# The call of configure is optional if you don't have special requirements +# If you wish to build zlib as a shared library, use: ./configure -s + +# To use the asm code, type: +# cp contrib/asm?86/match.S ./match.S +# make LOC=-DASMV OBJA=match.o + +# To install /usr/local/lib/libz.* and /usr/local/include/zlib.h, type: +# make install +# To install in $HOME instead of /usr/local, use: +# make install prefix=$HOME + +CC=cc + +CFLAGS=-O +#CFLAGS=-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7 +#CFLAGS=-g -DDEBUG +#CFLAGS=-O3 -Wall -Wwrite-strings -Wpointer-arith -Wconversion \ +# -Wstrict-prototypes -Wmissing-prototypes + +LDFLAGS=libz.a +LDSHARED=$(CC) +CPP=$(CC) -E + +LIBS=libz.a +SHAREDLIB=libz.so +SHAREDLIBV=libz.so.1.2.3 +SHAREDLIBM=libz.so.1 + +AR=ar rc +RANLIB=ranlib +TAR=tar +SHELL=/bin/sh +EXE= + +prefix = /usr/local +exec_prefix = ${prefix} +libdir = ${exec_prefix}/lib +includedir = ${prefix}/include +mandir = ${prefix}/share/man +man3dir = ${mandir}/man3 + +OBJS = adler32.o compress.o crc32.o gzio.o uncompr.o deflate.o trees.o \ + zutil.o inflate.o infback.o inftrees.o inffast.o + +OBJA = +# to use the asm code: make OBJA=match.o + +TEST_OBJS = example.o minigzip.o + +all: example$(EXE) minigzip$(EXE) + +check: test +test: all + @LD_LIBRARY_PATH=.:$(LD_LIBRARY_PATH) ; export LD_LIBRARY_PATH; \ + echo hello world | ./minigzip | ./minigzip -d || \ + echo ' *** minigzip test FAILED ***' ; \ + if ./example; then \ + echo ' *** zlib test OK ***'; \ + else \ + echo ' *** zlib test FAILED ***'; \ + fi + +libz.a: $(OBJS) $(OBJA) + $(AR) $@ $(OBJS) $(OBJA) + -@ ($(RANLIB) $@ || true) >/dev/null 2>&1 + +match.o: match.S + $(CPP) match.S > _match.s + $(CC) -c _match.s + mv _match.o match.o + rm -f _match.s + +$(SHAREDLIBV): $(OBJS) + $(LDSHARED) -o $@ $(OBJS) + rm -f $(SHAREDLIB) $(SHAREDLIBM) + ln -s $@ $(SHAREDLIB) + ln -s $@ $(SHAREDLIBM) + +example$(EXE): example.o $(LIBS) + $(CC) $(CFLAGS) -o $@ example.o $(LDFLAGS) + +minigzip$(EXE): minigzip.o $(LIBS) + $(CC) $(CFLAGS) -o $@ minigzip.o $(LDFLAGS) + +install: $(LIBS) + -@if [ ! -d $(exec_prefix) ]; then mkdir -p $(exec_prefix); fi + -@if [ ! -d $(includedir) ]; then mkdir -p $(includedir); fi + -@if [ ! -d $(libdir) ]; then mkdir -p $(libdir); fi + -@if [ ! -d $(man3dir) ]; then mkdir -p $(man3dir); fi + cp zlib.h zconf.h $(includedir) + chmod 644 $(includedir)/zlib.h $(includedir)/zconf.h + cp $(LIBS) $(libdir) + cd $(libdir); chmod 755 $(LIBS) + -@(cd $(libdir); $(RANLIB) libz.a || true) >/dev/null 2>&1 + cd $(libdir); if test -f $(SHAREDLIBV); then \ + rm -f $(SHAREDLIB) $(SHAREDLIBM); \ + ln -s $(SHAREDLIBV) $(SHAREDLIB); \ + ln -s $(SHAREDLIBV) $(SHAREDLIBM); \ + (ldconfig || true) >/dev/null 2>&1; \ + fi + cp zlib.3 $(man3dir) + chmod 644 $(man3dir)/zlib.3 +# The ranlib in install is needed on NeXTSTEP which checks file times +# ldconfig is for Linux + +uninstall: + cd $(includedir); \ + cd $(libdir); rm -f libz.a; \ + if test -f $(SHAREDLIBV); then \ + rm -f $(SHAREDLIBV) $(SHAREDLIB) $(SHAREDLIBM); \ + fi + cd $(man3dir); rm -f zlib.3 + +mostlyclean: clean +clean: + rm -f *.o *~ example$(EXE) minigzip$(EXE) \ + libz.* foo.gz so_locations \ + _match.s maketree contrib/infback9/*.o + +maintainer-clean: distclean +distclean: clean + cp -p Makefile.in Makefile + cp -p zconf.in.h zconf.h + rm -f .DS_Store + +tags: + etags *.[ch] + +depend: + makedepend -- $(CFLAGS) -- *.[ch] + +# DO NOT DELETE THIS LINE -- make depend depends on it. + +adler32.o: zlib.h zconf.h +compress.o: zlib.h zconf.h +crc32.o: crc32.h zlib.h zconf.h +deflate.o: deflate.h zutil.h zlib.h zconf.h +example.o: zlib.h zconf.h +gzio.o: zutil.h zlib.h zconf.h +inffast.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h +inflate.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h +infback.o: zutil.h zlib.h zconf.h inftrees.h inflate.h inffast.h +inftrees.o: zutil.h zlib.h zconf.h inftrees.h +minigzip.o: zlib.h zconf.h +trees.o: deflate.h zutil.h zlib.h zconf.h trees.h +uncompr.o: zlib.h zconf.h +zutil.o: zutil.h zlib.h zconf.h diff --git a/src/lib/zlib/adler32.c b/src/lib/zlib/adler32.c new file mode 100644 index 00000000000..007ba26277c --- /dev/null +++ b/src/lib/zlib/adler32.c @@ -0,0 +1,149 @@ +/* adler32.c -- compute the Adler-32 checksum of a data stream + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +#define BASE 65521UL /* largest prime smaller than 65536 */ +#define NMAX 5552 +/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ + +#define DO1(buf,i) {adler += (buf)[i]; sum2 += adler;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +/* use NO_DIVIDE if your processor does not do division in hardware */ +#ifdef NO_DIVIDE +# define MOD(a) \ + do { \ + if (a >= (BASE << 16)) a -= (BASE << 16); \ + if (a >= (BASE << 15)) a -= (BASE << 15); \ + if (a >= (BASE << 14)) a -= (BASE << 14); \ + if (a >= (BASE << 13)) a -= (BASE << 13); \ + if (a >= (BASE << 12)) a -= (BASE << 12); \ + if (a >= (BASE << 11)) a -= (BASE << 11); \ + if (a >= (BASE << 10)) a -= (BASE << 10); \ + if (a >= (BASE << 9)) a -= (BASE << 9); \ + if (a >= (BASE << 8)) a -= (BASE << 8); \ + if (a >= (BASE << 7)) a -= (BASE << 7); \ + if (a >= (BASE << 6)) a -= (BASE << 6); \ + if (a >= (BASE << 5)) a -= (BASE << 5); \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +# define MOD4(a) \ + do { \ + if (a >= (BASE << 4)) a -= (BASE << 4); \ + if (a >= (BASE << 3)) a -= (BASE << 3); \ + if (a >= (BASE << 2)) a -= (BASE << 2); \ + if (a >= (BASE << 1)) a -= (BASE << 1); \ + if (a >= BASE) a -= BASE; \ + } while (0) +#else +# define MOD(a) a %= BASE +# define MOD4(a) a %= BASE +#endif + +/* ========================================================================= */ +uLong ZEXPORT adler32(adler, buf, len) + uLong adler; + const Bytef *buf; + uInt len; +{ + unsigned long sum2; + unsigned n; + + /* split Adler-32 into component sums */ + sum2 = (adler >> 16) & 0xffff; + adler &= 0xffff; + + /* in case user likes doing a byte at a time, keep it fast */ + if (len == 1) { + adler += buf[0]; + if (adler >= BASE) + adler -= BASE; + sum2 += adler; + if (sum2 >= BASE) + sum2 -= BASE; + return adler | (sum2 << 16); + } + + /* initial Adler-32 value (deferred check for len == 1 speed) */ + if (buf == Z_NULL) + return 1L; + + /* in case short lengths are provided, keep it somewhat fast */ + if (len < 16) { + while (len--) { + adler += *buf++; + sum2 += adler; + } + if (adler >= BASE) + adler -= BASE; + MOD4(sum2); /* only added so many BASE's */ + return adler | (sum2 << 16); + } + + /* do length NMAX blocks -- requires just one modulo operation */ + while (len >= NMAX) { + len -= NMAX; + n = NMAX / 16; /* NMAX is divisible by 16 */ + do { + DO16(buf); /* 16 sums unrolled */ + buf += 16; + } while (--n); + MOD(adler); + MOD(sum2); + } + + /* do remaining bytes (less than NMAX, still just one modulo) */ + if (len) { /* avoid modulos if none remaining */ + while (len >= 16) { + len -= 16; + DO16(buf); + buf += 16; + } + while (len--) { + adler += *buf++; + sum2 += adler; + } + MOD(adler); + MOD(sum2); + } + + /* return recombined sums */ + return adler | (sum2 << 16); +} + +/* ========================================================================= */ +uLong ZEXPORT adler32_combine(adler1, adler2, len2) + uLong adler1; + uLong adler2; + z_off_t len2; +{ + unsigned long sum1; + unsigned long sum2; + unsigned rem; + + /* the derivation of this formula is left as an exercise for the reader */ + rem = (unsigned)(len2 % BASE); + sum1 = adler1 & 0xffff; + sum2 = rem * sum1; + MOD(sum2); + sum1 += (adler2 & 0xffff) + BASE - 1; + sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; + if (sum1 > BASE) sum1 -= BASE; + if (sum1 > BASE) sum1 -= BASE; + if (sum2 > (BASE << 1)) sum2 -= (BASE << 1); + if (sum2 > BASE) sum2 -= BASE; + return sum1 | (sum2 << 16); +} diff --git a/src/lib/zlib/compress.c b/src/lib/zlib/compress.c new file mode 100644 index 00000000000..df04f0148e6 --- /dev/null +++ b/src/lib/zlib/compress.c @@ -0,0 +1,79 @@ +/* compress.c -- compress a memory buffer + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least 0.1% larger than sourceLen plus + 12 bytes. Upon exit, destLen is the actual size of the compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ +int ZEXPORT compress2 (dest, destLen, source, sourceLen, level) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; + int level; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; +#ifdef MAXSEG_64K + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; +#endif + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + stream.opaque = (voidpf)0; + + err = deflateInit(&stream, level); + if (err != Z_OK) return err; + + err = deflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + deflateEnd(&stream); + return err == Z_OK ? Z_BUF_ERROR : err; + } + *destLen = stream.total_out; + + err = deflateEnd(&stream); + return err; +} + +/* =========================================================================== + */ +int ZEXPORT compress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + return compress2(dest, destLen, source, sourceLen, Z_DEFAULT_COMPRESSION); +} + +/* =========================================================================== + If the default memLevel or windowBits for deflateInit() is changed, then + this function needs to be updated. + */ +uLong ZEXPORT compressBound (sourceLen) + uLong sourceLen; +{ + return sourceLen + (sourceLen >> 12) + (sourceLen >> 14) + 11; +} diff --git a/src/lib/zlib/crc32.c b/src/lib/zlib/crc32.c new file mode 100644 index 00000000000..f658a9ef55e --- /dev/null +++ b/src/lib/zlib/crc32.c @@ -0,0 +1,423 @@ +/* crc32.c -- compute the CRC-32 of a data stream + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Thanks to Rodney Brown <rbrown64@csc.com.au> for his contribution of faster + * CRC methods: exclusive-oring 32 bits of data at a time, and pre-computing + * tables for updating the shift register in one step with three exclusive-ors + * instead of four steps with four exclusive-ors. This results in about a + * factor of two increase in speed on a Power PC G4 (PPC7455) using gcc -O3. + */ + +/* @(#) $Id$ */ + +/* + Note on the use of DYNAMIC_CRC_TABLE: there is no mutex or semaphore + protection on the static variables used to control the first-use generation + of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should + first call get_crc_table() to initialize the tables before allowing more than + one thread to use crc32(). + */ + +#ifdef MAKECRCH +# include <stdio.h> +# ifndef DYNAMIC_CRC_TABLE +# define DYNAMIC_CRC_TABLE +# endif /* !DYNAMIC_CRC_TABLE */ +#endif /* MAKECRCH */ + +#include "zutil.h" /* for STDC and FAR definitions */ + +#define local static + +/* Find a four-byte integer type for crc32_little() and crc32_big(). */ +#ifndef NOBYFOUR +# ifdef STDC /* need ANSI C limits.h to determine sizes */ +# include <limits.h> +# define BYFOUR +# if (UINT_MAX == 0xffffffffUL) + typedef unsigned int u4; +# else +# if (ULONG_MAX == 0xffffffffUL) + typedef unsigned long u4; +# else +# if (USHRT_MAX == 0xffffffffUL) + typedef unsigned short u4; +# else +# undef BYFOUR /* can't find a four-byte integer type! */ +# endif +# endif +# endif +# endif /* STDC */ +#endif /* !NOBYFOUR */ + +/* Definitions for doing the crc four data bytes at a time. */ +#ifdef BYFOUR +# define REV(w) (((w)>>24)+(((w)>>8)&0xff00)+ \ + (((w)&0xff00)<<8)+(((w)&0xff)<<24)) + local unsigned long crc32_little OF((unsigned long, + const unsigned char FAR *, unsigned)); + local unsigned long crc32_big OF((unsigned long, + const unsigned char FAR *, unsigned)); +# define TBLS 8 +#else +# define TBLS 1 +#endif /* BYFOUR */ + +/* Local functions for crc concatenation */ +local unsigned long gf2_matrix_times OF((unsigned long *mat, + unsigned long vec)); +local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); + +#ifdef DYNAMIC_CRC_TABLE + +local volatile int crc_table_empty = 1; +local unsigned long FAR crc_table[TBLS][256]; +local void make_crc_table OF((void)); +#ifdef MAKECRCH + local void write_table OF((FILE *, const unsigned long FAR *)); +#endif /* MAKECRCH */ +/* + Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: + x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1. + + Polynomials over GF(2) are represented in binary, one bit per coefficient, + with the lowest powers in the most significant bit. Then adding polynomials + is just exclusive-or, and multiplying a polynomial by x is a right shift by + one. If we call the above polynomial p, and represent a byte as the + polynomial q, also with the lowest power in the most significant bit (so the + byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p, + where a mod b means the remainder after dividing a by b. + + This calculation is done using the shift-register method of multiplying and + taking the remainder. The register is initialized to zero, and for each + incoming bit, x^32 is added mod p to the register if the bit is a one (where + x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by + x (which is shifting right by one and adding x^32 mod p if the bit shifted + out is a one). We start with the highest power (least significant bit) of + q and repeat for all eight bits of q. + + The first table is simply the CRC of all possible eight bit values. This is + all the information needed to generate CRCs on data a byte at a time for all + combinations of CRC register values and incoming bytes. The remaining tables + allow for word-at-a-time CRC calculation for both big-endian and little- + endian machines, where a word is four bytes. +*/ +local void make_crc_table() +{ + unsigned long c; + int n, k; + unsigned long poly; /* polynomial exclusive-or pattern */ + /* terms of polynomial defining this crc (except x^32): */ + static volatile int first = 1; /* flag to limit concurrent making */ + static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; + + /* See if another task is already doing this (not thread-safe, but better + than nothing -- significantly reduces duration of vulnerability in + case the advice about DYNAMIC_CRC_TABLE is ignored) */ + if (first) { + first = 0; + + /* make exclusive-or pattern from polynomial (0xedb88320UL) */ + poly = 0UL; + for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++) + poly |= 1UL << (31 - p[n]); + + /* generate a crc for every 8-bit value */ + for (n = 0; n < 256; n++) { + c = (unsigned long)n; + for (k = 0; k < 8; k++) + c = c & 1 ? poly ^ (c >> 1) : c >> 1; + crc_table[0][n] = c; + } + +#ifdef BYFOUR + /* generate crc for each value followed by one, two, and three zeros, + and then the byte reversal of those as well as the first table */ + for (n = 0; n < 256; n++) { + c = crc_table[0][n]; + crc_table[4][n] = REV(c); + for (k = 1; k < 4; k++) { + c = crc_table[0][c & 0xff] ^ (c >> 8); + crc_table[k][n] = c; + crc_table[k + 4][n] = REV(c); + } + } +#endif /* BYFOUR */ + + crc_table_empty = 0; + } + else { /* not first */ + /* wait for the other guy to finish (not efficient, but rare) */ + while (crc_table_empty) + ; + } + +#ifdef MAKECRCH + /* write out CRC tables to crc32.h */ + { + FILE *out; + + out = fopen("crc32.h", "w"); + if (out == NULL) return; + fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); + fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); + fprintf(out, "local const unsigned long FAR "); + fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); + write_table(out, crc_table[0]); +# ifdef BYFOUR + fprintf(out, "#ifdef BYFOUR\n"); + for (k = 1; k < 8; k++) { + fprintf(out, " },\n {\n"); + write_table(out, crc_table[k]); + } + fprintf(out, "#endif\n"); +# endif /* BYFOUR */ + fprintf(out, " }\n};\n"); + fclose(out); + } +#endif /* MAKECRCH */ +} + +#ifdef MAKECRCH +local void write_table(out, table) + FILE *out; + const unsigned long FAR *table; +{ + int n; + + for (n = 0; n < 256; n++) + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n], + n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); +} +#endif /* MAKECRCH */ + +#else /* !DYNAMIC_CRC_TABLE */ +/* ======================================================================== + * Tables of CRC-32s of all single-byte values, made by make_crc_table(). + */ +#include "crc32.h" +#endif /* DYNAMIC_CRC_TABLE */ + +/* ========================================================================= + * This function can be used by asm versions of crc32() + */ +const unsigned long FAR * ZEXPORT get_crc_table() +{ +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + return (const unsigned long FAR *)crc_table; +} + +/* ========================================================================= */ +#define DO1 crc = crc_table[0][((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8) +#define DO8 DO1; DO1; DO1; DO1; DO1; DO1; DO1; DO1 + +/* ========================================================================= */ +unsigned long ZEXPORT crc32(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + if (buf == Z_NULL) return 0UL; + +#ifdef DYNAMIC_CRC_TABLE + if (crc_table_empty) + make_crc_table(); +#endif /* DYNAMIC_CRC_TABLE */ + +#ifdef BYFOUR + if (sizeof(void *) == sizeof(ptrdiff_t)) { + u4 endian; + + endian = 1; + if (*((unsigned char *)(&endian))) + return crc32_little(crc, buf, len); + else + return crc32_big(crc, buf, len); + } +#endif /* BYFOUR */ + crc = crc ^ 0xffffffffUL; + while (len >= 8) { + DO8; + len -= 8; + } + if (len) do { + DO1; + } while (--len); + return crc ^ 0xffffffffUL; +} + +#ifdef BYFOUR + +/* ========================================================================= */ +#define DOLIT4 c ^= *buf4++; \ + c = crc_table[3][c & 0xff] ^ crc_table[2][(c >> 8) & 0xff] ^ \ + crc_table[1][(c >> 16) & 0xff] ^ crc_table[0][c >> 24] +#define DOLIT32 DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4; DOLIT4 + +/* ========================================================================= */ +local unsigned long crc32_little(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = (u4)crc; + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + while (len >= 32) { + DOLIT32; + len -= 32; + } + while (len >= 4) { + DOLIT4; + len -= 4; + } + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); + } while (--len); + c = ~c; + return (unsigned long)c; +} + +/* ========================================================================= */ +#define DOBIG4 c ^= *++buf4; \ + c = crc_table[4][c & 0xff] ^ crc_table[5][(c >> 8) & 0xff] ^ \ + crc_table[6][(c >> 16) & 0xff] ^ crc_table[7][c >> 24] +#define DOBIG32 DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4; DOBIG4 + +/* ========================================================================= */ +local unsigned long crc32_big(crc, buf, len) + unsigned long crc; + const unsigned char FAR *buf; + unsigned len; +{ + register u4 c; + register const u4 FAR *buf4; + + c = REV((u4)crc); + c = ~c; + while (len && ((ptrdiff_t)buf & 3)) { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + len--; + } + + buf4 = (const u4 FAR *)(const void FAR *)buf; + buf4--; + while (len >= 32) { + DOBIG32; + len -= 32; + } + while (len >= 4) { + DOBIG4; + len -= 4; + } + buf4++; + buf = (const unsigned char FAR *)buf4; + + if (len) do { + c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); + } while (--len); + c = ~c; + return (unsigned long)(REV(c)); +} + +#endif /* BYFOUR */ + +#define GF2_DIM 32 /* dimension of GF(2) vectors (length of CRC) */ + +/* ========================================================================= */ +local unsigned long gf2_matrix_times(mat, vec) + unsigned long *mat; + unsigned long vec; +{ + unsigned long sum; + + sum = 0; + while (vec) { + if (vec & 1) + sum ^= *mat; + vec >>= 1; + mat++; + } + return sum; +} + +/* ========================================================================= */ +local void gf2_matrix_square(square, mat) + unsigned long *square; + unsigned long *mat; +{ + int n; + + for (n = 0; n < GF2_DIM; n++) + square[n] = gf2_matrix_times(mat, mat[n]); +} + +/* ========================================================================= */ +uLong ZEXPORT crc32_combine(crc1, crc2, len2) + uLong crc1; + uLong crc2; + z_off_t len2; +{ + int n; + unsigned long row; + unsigned long even[GF2_DIM]; /* even-power-of-two zeros operator */ + unsigned long odd[GF2_DIM]; /* odd-power-of-two zeros operator */ + + /* degenerate case */ + if (len2 == 0) + return crc1; + + /* put operator for one zero bit in odd */ + odd[0] = 0xedb88320L; /* CRC-32 polynomial */ + row = 1; + for (n = 1; n < GF2_DIM; n++) { + odd[n] = row; + row <<= 1; + } + + /* put operator for two zero bits in even */ + gf2_matrix_square(even, odd); + + /* put operator for four zero bits in odd */ + gf2_matrix_square(odd, even); + + /* apply len2 zeros to crc1 (first square will put the operator for one + zero byte, eight zero bits, in even) */ + do { + /* apply zeros operator for this bit of len2 */ + gf2_matrix_square(even, odd); + if (len2 & 1) + crc1 = gf2_matrix_times(even, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + if (len2 == 0) + break; + + /* another iteration of the loop with odd and even swapped */ + gf2_matrix_square(odd, even); + if (len2 & 1) + crc1 = gf2_matrix_times(odd, crc1); + len2 >>= 1; + + /* if no more bits set, then done */ + } while (len2 != 0); + + /* return combined crc */ + crc1 ^= crc2; + return crc1; +} diff --git a/src/lib/zlib/crc32.h b/src/lib/zlib/crc32.h new file mode 100644 index 00000000000..8053b6117c0 --- /dev/null +++ b/src/lib/zlib/crc32.h @@ -0,0 +1,441 @@ +/* crc32.h -- tables for rapid CRC calculation + * Generated automatically by crc32.c + */ + +local const unsigned long FAR crc_table[TBLS][256] = +{ + { + 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, + 0x706af48fUL, 0xe963a535UL, 0x9e6495a3UL, 0x0edb8832UL, 0x79dcb8a4UL, + 0xe0d5e91eUL, 0x97d2d988UL, 0x09b64c2bUL, 0x7eb17cbdUL, 0xe7b82d07UL, + 0x90bf1d91UL, 0x1db71064UL, 0x6ab020f2UL, 0xf3b97148UL, 0x84be41deUL, + 0x1adad47dUL, 0x6ddde4ebUL, 0xf4d4b551UL, 0x83d385c7UL, 0x136c9856UL, + 0x646ba8c0UL, 0xfd62f97aUL, 0x8a65c9ecUL, 0x14015c4fUL, 0x63066cd9UL, + 0xfa0f3d63UL, 0x8d080df5UL, 0x3b6e20c8UL, 0x4c69105eUL, 0xd56041e4UL, + 0xa2677172UL, 0x3c03e4d1UL, 0x4b04d447UL, 0xd20d85fdUL, 0xa50ab56bUL, + 0x35b5a8faUL, 0x42b2986cUL, 0xdbbbc9d6UL, 0xacbcf940UL, 0x32d86ce3UL, + 0x45df5c75UL, 0xdcd60dcfUL, 0xabd13d59UL, 0x26d930acUL, 0x51de003aUL, + 0xc8d75180UL, 0xbfd06116UL, 0x21b4f4b5UL, 0x56b3c423UL, 0xcfba9599UL, + 0xb8bda50fUL, 0x2802b89eUL, 0x5f058808UL, 0xc60cd9b2UL, 0xb10be924UL, + 0x2f6f7c87UL, 0x58684c11UL, 0xc1611dabUL, 0xb6662d3dUL, 0x76dc4190UL, + 0x01db7106UL, 0x98d220bcUL, 0xefd5102aUL, 0x71b18589UL, 0x06b6b51fUL, + 0x9fbfe4a5UL, 0xe8b8d433UL, 0x7807c9a2UL, 0x0f00f934UL, 0x9609a88eUL, + 0xe10e9818UL, 0x7f6a0dbbUL, 0x086d3d2dUL, 0x91646c97UL, 0xe6635c01UL, + 0x6b6b51f4UL, 0x1c6c6162UL, 0x856530d8UL, 0xf262004eUL, 0x6c0695edUL, + 0x1b01a57bUL, 0x8208f4c1UL, 0xf50fc457UL, 0x65b0d9c6UL, 0x12b7e950UL, + 0x8bbeb8eaUL, 0xfcb9887cUL, 0x62dd1ddfUL, 0x15da2d49UL, 0x8cd37cf3UL, + 0xfbd44c65UL, 0x4db26158UL, 0x3ab551ceUL, 0xa3bc0074UL, 0xd4bb30e2UL, + 0x4adfa541UL, 0x3dd895d7UL, 0xa4d1c46dUL, 0xd3d6f4fbUL, 0x4369e96aUL, + 0x346ed9fcUL, 0xad678846UL, 0xda60b8d0UL, 0x44042d73UL, 0x33031de5UL, + 0xaa0a4c5fUL, 0xdd0d7cc9UL, 0x5005713cUL, 0x270241aaUL, 0xbe0b1010UL, + 0xc90c2086UL, 0x5768b525UL, 0x206f85b3UL, 0xb966d409UL, 0xce61e49fUL, + 0x5edef90eUL, 0x29d9c998UL, 0xb0d09822UL, 0xc7d7a8b4UL, 0x59b33d17UL, + 0x2eb40d81UL, 0xb7bd5c3bUL, 0xc0ba6cadUL, 0xedb88320UL, 0x9abfb3b6UL, + 0x03b6e20cUL, 0x74b1d29aUL, 0xead54739UL, 0x9dd277afUL, 0x04db2615UL, + 0x73dc1683UL, 0xe3630b12UL, 0x94643b84UL, 0x0d6d6a3eUL, 0x7a6a5aa8UL, + 0xe40ecf0bUL, 0x9309ff9dUL, 0x0a00ae27UL, 0x7d079eb1UL, 0xf00f9344UL, + 0x8708a3d2UL, 0x1e01f268UL, 0x6906c2feUL, 0xf762575dUL, 0x806567cbUL, + 0x196c3671UL, 0x6e6b06e7UL, 0xfed41b76UL, 0x89d32be0UL, 0x10da7a5aUL, + 0x67dd4accUL, 0xf9b9df6fUL, 0x8ebeeff9UL, 0x17b7be43UL, 0x60b08ed5UL, + 0xd6d6a3e8UL, 0xa1d1937eUL, 0x38d8c2c4UL, 0x4fdff252UL, 0xd1bb67f1UL, + 0xa6bc5767UL, 0x3fb506ddUL, 0x48b2364bUL, 0xd80d2bdaUL, 0xaf0a1b4cUL, + 0x36034af6UL, 0x41047a60UL, 0xdf60efc3UL, 0xa867df55UL, 0x316e8eefUL, + 0x4669be79UL, 0xcb61b38cUL, 0xbc66831aUL, 0x256fd2a0UL, 0x5268e236UL, + 0xcc0c7795UL, 0xbb0b4703UL, 0x220216b9UL, 0x5505262fUL, 0xc5ba3bbeUL, + 0xb2bd0b28UL, 0x2bb45a92UL, 0x5cb36a04UL, 0xc2d7ffa7UL, 0xb5d0cf31UL, + 0x2cd99e8bUL, 0x5bdeae1dUL, 0x9b64c2b0UL, 0xec63f226UL, 0x756aa39cUL, + 0x026d930aUL, 0x9c0906a9UL, 0xeb0e363fUL, 0x72076785UL, 0x05005713UL, + 0x95bf4a82UL, 0xe2b87a14UL, 0x7bb12baeUL, 0x0cb61b38UL, 0x92d28e9bUL, + 0xe5d5be0dUL, 0x7cdcefb7UL, 0x0bdbdf21UL, 0x86d3d2d4UL, 0xf1d4e242UL, + 0x68ddb3f8UL, 0x1fda836eUL, 0x81be16cdUL, 0xf6b9265bUL, 0x6fb077e1UL, + 0x18b74777UL, 0x88085ae6UL, 0xff0f6a70UL, 0x66063bcaUL, 0x11010b5cUL, + 0x8f659effUL, 0xf862ae69UL, 0x616bffd3UL, 0x166ccf45UL, 0xa00ae278UL, + 0xd70dd2eeUL, 0x4e048354UL, 0x3903b3c2UL, 0xa7672661UL, 0xd06016f7UL, + 0x4969474dUL, 0x3e6e77dbUL, 0xaed16a4aUL, 0xd9d65adcUL, 0x40df0b66UL, + 0x37d83bf0UL, 0xa9bcae53UL, 0xdebb9ec5UL, 0x47b2cf7fUL, 0x30b5ffe9UL, + 0xbdbdf21cUL, 0xcabac28aUL, 0x53b39330UL, 0x24b4a3a6UL, 0xbad03605UL, + 0xcdd70693UL, 0x54de5729UL, 0x23d967bfUL, 0xb3667a2eUL, 0xc4614ab8UL, + 0x5d681b02UL, 0x2a6f2b94UL, 0xb40bbe37UL, 0xc30c8ea1UL, 0x5a05df1bUL, + 0x2d02ef8dUL +#ifdef BYFOUR + }, + { + 0x00000000UL, 0x191b3141UL, 0x32366282UL, 0x2b2d53c3UL, 0x646cc504UL, + 0x7d77f445UL, 0x565aa786UL, 0x4f4196c7UL, 0xc8d98a08UL, 0xd1c2bb49UL, + 0xfaefe88aUL, 0xe3f4d9cbUL, 0xacb54f0cUL, 0xb5ae7e4dUL, 0x9e832d8eUL, + 0x87981ccfUL, 0x4ac21251UL, 0x53d92310UL, 0x78f470d3UL, 0x61ef4192UL, + 0x2eaed755UL, 0x37b5e614UL, 0x1c98b5d7UL, 0x05838496UL, 0x821b9859UL, + 0x9b00a918UL, 0xb02dfadbUL, 0xa936cb9aUL, 0xe6775d5dUL, 0xff6c6c1cUL, + 0xd4413fdfUL, 0xcd5a0e9eUL, 0x958424a2UL, 0x8c9f15e3UL, 0xa7b24620UL, + 0xbea97761UL, 0xf1e8e1a6UL, 0xe8f3d0e7UL, 0xc3de8324UL, 0xdac5b265UL, + 0x5d5daeaaUL, 0x44469febUL, 0x6f6bcc28UL, 0x7670fd69UL, 0x39316baeUL, + 0x202a5aefUL, 0x0b07092cUL, 0x121c386dUL, 0xdf4636f3UL, 0xc65d07b2UL, + 0xed705471UL, 0xf46b6530UL, 0xbb2af3f7UL, 0xa231c2b6UL, 0x891c9175UL, + 0x9007a034UL, 0x179fbcfbUL, 0x0e848dbaUL, 0x25a9de79UL, 0x3cb2ef38UL, + 0x73f379ffUL, 0x6ae848beUL, 0x41c51b7dUL, 0x58de2a3cUL, 0xf0794f05UL, + 0xe9627e44UL, 0xc24f2d87UL, 0xdb541cc6UL, 0x94158a01UL, 0x8d0ebb40UL, + 0xa623e883UL, 0xbf38d9c2UL, 0x38a0c50dUL, 0x21bbf44cUL, 0x0a96a78fUL, + 0x138d96ceUL, 0x5ccc0009UL, 0x45d73148UL, 0x6efa628bUL, 0x77e153caUL, + 0xbabb5d54UL, 0xa3a06c15UL, 0x888d3fd6UL, 0x91960e97UL, 0xded79850UL, + 0xc7cca911UL, 0xece1fad2UL, 0xf5facb93UL, 0x7262d75cUL, 0x6b79e61dUL, + 0x4054b5deUL, 0x594f849fUL, 0x160e1258UL, 0x0f152319UL, 0x243870daUL, + 0x3d23419bUL, 0x65fd6ba7UL, 0x7ce65ae6UL, 0x57cb0925UL, 0x4ed03864UL, + 0x0191aea3UL, 0x188a9fe2UL, 0x33a7cc21UL, 0x2abcfd60UL, 0xad24e1afUL, + 0xb43fd0eeUL, 0x9f12832dUL, 0x8609b26cUL, 0xc94824abUL, 0xd05315eaUL, + 0xfb7e4629UL, 0xe2657768UL, 0x2f3f79f6UL, 0x362448b7UL, 0x1d091b74UL, + 0x04122a35UL, 0x4b53bcf2UL, 0x52488db3UL, 0x7965de70UL, 0x607eef31UL, + 0xe7e6f3feUL, 0xfefdc2bfUL, 0xd5d0917cUL, 0xcccba03dUL, 0x838a36faUL, + 0x9a9107bbUL, 0xb1bc5478UL, 0xa8a76539UL, 0x3b83984bUL, 0x2298a90aUL, + 0x09b5fac9UL, 0x10aecb88UL, 0x5fef5d4fUL, 0x46f46c0eUL, 0x6dd93fcdUL, + 0x74c20e8cUL, 0xf35a1243UL, 0xea412302UL, 0xc16c70c1UL, 0xd8774180UL, + 0x9736d747UL, 0x8e2de606UL, 0xa500b5c5UL, 0xbc1b8484UL, 0x71418a1aUL, + 0x685abb5bUL, 0x4377e898UL, 0x5a6cd9d9UL, 0x152d4f1eUL, 0x0c367e5fUL, + 0x271b2d9cUL, 0x3e001cddUL, 0xb9980012UL, 0xa0833153UL, 0x8bae6290UL, + 0x92b553d1UL, 0xddf4c516UL, 0xc4eff457UL, 0xefc2a794UL, 0xf6d996d5UL, + 0xae07bce9UL, 0xb71c8da8UL, 0x9c31de6bUL, 0x852aef2aUL, 0xca6b79edUL, + 0xd37048acUL, 0xf85d1b6fUL, 0xe1462a2eUL, 0x66de36e1UL, 0x7fc507a0UL, + 0x54e85463UL, 0x4df36522UL, 0x02b2f3e5UL, 0x1ba9c2a4UL, 0x30849167UL, + 0x299fa026UL, 0xe4c5aeb8UL, 0xfdde9ff9UL, 0xd6f3cc3aUL, 0xcfe8fd7bUL, + 0x80a96bbcUL, 0x99b25afdUL, 0xb29f093eUL, 0xab84387fUL, 0x2c1c24b0UL, + 0x350715f1UL, 0x1e2a4632UL, 0x07317773UL, 0x4870e1b4UL, 0x516bd0f5UL, + 0x7a468336UL, 0x635db277UL, 0xcbfad74eUL, 0xd2e1e60fUL, 0xf9ccb5ccUL, + 0xe0d7848dUL, 0xaf96124aUL, 0xb68d230bUL, 0x9da070c8UL, 0x84bb4189UL, + 0x03235d46UL, 0x1a386c07UL, 0x31153fc4UL, 0x280e0e85UL, 0x674f9842UL, + 0x7e54a903UL, 0x5579fac0UL, 0x4c62cb81UL, 0x8138c51fUL, 0x9823f45eUL, + 0xb30ea79dUL, 0xaa1596dcUL, 0xe554001bUL, 0xfc4f315aUL, 0xd7626299UL, + 0xce7953d8UL, 0x49e14f17UL, 0x50fa7e56UL, 0x7bd72d95UL, 0x62cc1cd4UL, + 0x2d8d8a13UL, 0x3496bb52UL, 0x1fbbe891UL, 0x06a0d9d0UL, 0x5e7ef3ecUL, + 0x4765c2adUL, 0x6c48916eUL, 0x7553a02fUL, 0x3a1236e8UL, 0x230907a9UL, + 0x0824546aUL, 0x113f652bUL, 0x96a779e4UL, 0x8fbc48a5UL, 0xa4911b66UL, + 0xbd8a2a27UL, 0xf2cbbce0UL, 0xebd08da1UL, 0xc0fdde62UL, 0xd9e6ef23UL, + 0x14bce1bdUL, 0x0da7d0fcUL, 0x268a833fUL, 0x3f91b27eUL, 0x70d024b9UL, + 0x69cb15f8UL, 0x42e6463bUL, 0x5bfd777aUL, 0xdc656bb5UL, 0xc57e5af4UL, + 0xee530937UL, 0xf7483876UL, 0xb809aeb1UL, 0xa1129ff0UL, 0x8a3fcc33UL, + 0x9324fd72UL + }, + { + 0x00000000UL, 0x01c26a37UL, 0x0384d46eUL, 0x0246be59UL, 0x0709a8dcUL, + 0x06cbc2ebUL, 0x048d7cb2UL, 0x054f1685UL, 0x0e1351b8UL, 0x0fd13b8fUL, + 0x0d9785d6UL, 0x0c55efe1UL, 0x091af964UL, 0x08d89353UL, 0x0a9e2d0aUL, + 0x0b5c473dUL, 0x1c26a370UL, 0x1de4c947UL, 0x1fa2771eUL, 0x1e601d29UL, + 0x1b2f0bacUL, 0x1aed619bUL, 0x18abdfc2UL, 0x1969b5f5UL, 0x1235f2c8UL, + 0x13f798ffUL, 0x11b126a6UL, 0x10734c91UL, 0x153c5a14UL, 0x14fe3023UL, + 0x16b88e7aUL, 0x177ae44dUL, 0x384d46e0UL, 0x398f2cd7UL, 0x3bc9928eUL, + 0x3a0bf8b9UL, 0x3f44ee3cUL, 0x3e86840bUL, 0x3cc03a52UL, 0x3d025065UL, + 0x365e1758UL, 0x379c7d6fUL, 0x35dac336UL, 0x3418a901UL, 0x3157bf84UL, + 0x3095d5b3UL, 0x32d36beaUL, 0x331101ddUL, 0x246be590UL, 0x25a98fa7UL, + 0x27ef31feUL, 0x262d5bc9UL, 0x23624d4cUL, 0x22a0277bUL, 0x20e69922UL, + 0x2124f315UL, 0x2a78b428UL, 0x2bbade1fUL, 0x29fc6046UL, 0x283e0a71UL, + 0x2d711cf4UL, 0x2cb376c3UL, 0x2ef5c89aUL, 0x2f37a2adUL, 0x709a8dc0UL, + 0x7158e7f7UL, 0x731e59aeUL, 0x72dc3399UL, 0x7793251cUL, 0x76514f2bUL, + 0x7417f172UL, 0x75d59b45UL, 0x7e89dc78UL, 0x7f4bb64fUL, 0x7d0d0816UL, + 0x7ccf6221UL, 0x798074a4UL, 0x78421e93UL, 0x7a04a0caUL, 0x7bc6cafdUL, + 0x6cbc2eb0UL, 0x6d7e4487UL, 0x6f38fadeUL, 0x6efa90e9UL, 0x6bb5866cUL, + 0x6a77ec5bUL, 0x68315202UL, 0x69f33835UL, 0x62af7f08UL, 0x636d153fUL, + 0x612bab66UL, 0x60e9c151UL, 0x65a6d7d4UL, 0x6464bde3UL, 0x662203baUL, + 0x67e0698dUL, 0x48d7cb20UL, 0x4915a117UL, 0x4b531f4eUL, 0x4a917579UL, + 0x4fde63fcUL, 0x4e1c09cbUL, 0x4c5ab792UL, 0x4d98dda5UL, 0x46c49a98UL, + 0x4706f0afUL, 0x45404ef6UL, 0x448224c1UL, 0x41cd3244UL, 0x400f5873UL, + 0x4249e62aUL, 0x438b8c1dUL, 0x54f16850UL, 0x55330267UL, 0x5775bc3eUL, + 0x56b7d609UL, 0x53f8c08cUL, 0x523aaabbUL, 0x507c14e2UL, 0x51be7ed5UL, + 0x5ae239e8UL, 0x5b2053dfUL, 0x5966ed86UL, 0x58a487b1UL, 0x5deb9134UL, + 0x5c29fb03UL, 0x5e6f455aUL, 0x5fad2f6dUL, 0xe1351b80UL, 0xe0f771b7UL, + 0xe2b1cfeeUL, 0xe373a5d9UL, 0xe63cb35cUL, 0xe7fed96bUL, 0xe5b86732UL, + 0xe47a0d05UL, 0xef264a38UL, 0xeee4200fUL, 0xeca29e56UL, 0xed60f461UL, + 0xe82fe2e4UL, 0xe9ed88d3UL, 0xebab368aUL, 0xea695cbdUL, 0xfd13b8f0UL, + 0xfcd1d2c7UL, 0xfe976c9eUL, 0xff5506a9UL, 0xfa1a102cUL, 0xfbd87a1bUL, + 0xf99ec442UL, 0xf85cae75UL, 0xf300e948UL, 0xf2c2837fUL, 0xf0843d26UL, + 0xf1465711UL, 0xf4094194UL, 0xf5cb2ba3UL, 0xf78d95faUL, 0xf64fffcdUL, + 0xd9785d60UL, 0xd8ba3757UL, 0xdafc890eUL, 0xdb3ee339UL, 0xde71f5bcUL, + 0xdfb39f8bUL, 0xddf521d2UL, 0xdc374be5UL, 0xd76b0cd8UL, 0xd6a966efUL, + 0xd4efd8b6UL, 0xd52db281UL, 0xd062a404UL, 0xd1a0ce33UL, 0xd3e6706aUL, + 0xd2241a5dUL, 0xc55efe10UL, 0xc49c9427UL, 0xc6da2a7eUL, 0xc7184049UL, + 0xc25756ccUL, 0xc3953cfbUL, 0xc1d382a2UL, 0xc011e895UL, 0xcb4dafa8UL, + 0xca8fc59fUL, 0xc8c97bc6UL, 0xc90b11f1UL, 0xcc440774UL, 0xcd866d43UL, + 0xcfc0d31aUL, 0xce02b92dUL, 0x91af9640UL, 0x906dfc77UL, 0x922b422eUL, + 0x93e92819UL, 0x96a63e9cUL, 0x976454abUL, 0x9522eaf2UL, 0x94e080c5UL, + 0x9fbcc7f8UL, 0x9e7eadcfUL, 0x9c381396UL, 0x9dfa79a1UL, 0x98b56f24UL, + 0x99770513UL, 0x9b31bb4aUL, 0x9af3d17dUL, 0x8d893530UL, 0x8c4b5f07UL, + 0x8e0de15eUL, 0x8fcf8b69UL, 0x8a809decUL, 0x8b42f7dbUL, 0x89044982UL, + 0x88c623b5UL, 0x839a6488UL, 0x82580ebfUL, 0x801eb0e6UL, 0x81dcdad1UL, + 0x8493cc54UL, 0x8551a663UL, 0x8717183aUL, 0x86d5720dUL, 0xa9e2d0a0UL, + 0xa820ba97UL, 0xaa6604ceUL, 0xaba46ef9UL, 0xaeeb787cUL, 0xaf29124bUL, + 0xad6fac12UL, 0xacadc625UL, 0xa7f18118UL, 0xa633eb2fUL, 0xa4755576UL, + 0xa5b73f41UL, 0xa0f829c4UL, 0xa13a43f3UL, 0xa37cfdaaUL, 0xa2be979dUL, + 0xb5c473d0UL, 0xb40619e7UL, 0xb640a7beUL, 0xb782cd89UL, 0xb2cddb0cUL, + 0xb30fb13bUL, 0xb1490f62UL, 0xb08b6555UL, 0xbbd72268UL, 0xba15485fUL, + 0xb853f606UL, 0xb9919c31UL, 0xbcde8ab4UL, 0xbd1ce083UL, 0xbf5a5edaUL, + 0xbe9834edUL + }, + { + 0x00000000UL, 0xb8bc6765UL, 0xaa09c88bUL, 0x12b5afeeUL, 0x8f629757UL, + 0x37def032UL, 0x256b5fdcUL, 0x9dd738b9UL, 0xc5b428efUL, 0x7d084f8aUL, + 0x6fbde064UL, 0xd7018701UL, 0x4ad6bfb8UL, 0xf26ad8ddUL, 0xe0df7733UL, + 0x58631056UL, 0x5019579fUL, 0xe8a530faUL, 0xfa109f14UL, 0x42acf871UL, + 0xdf7bc0c8UL, 0x67c7a7adUL, 0x75720843UL, 0xcdce6f26UL, 0x95ad7f70UL, + 0x2d111815UL, 0x3fa4b7fbUL, 0x8718d09eUL, 0x1acfe827UL, 0xa2738f42UL, + 0xb0c620acUL, 0x087a47c9UL, 0xa032af3eUL, 0x188ec85bUL, 0x0a3b67b5UL, + 0xb28700d0UL, 0x2f503869UL, 0x97ec5f0cUL, 0x8559f0e2UL, 0x3de59787UL, + 0x658687d1UL, 0xdd3ae0b4UL, 0xcf8f4f5aUL, 0x7733283fUL, 0xeae41086UL, + 0x525877e3UL, 0x40edd80dUL, 0xf851bf68UL, 0xf02bf8a1UL, 0x48979fc4UL, + 0x5a22302aUL, 0xe29e574fUL, 0x7f496ff6UL, 0xc7f50893UL, 0xd540a77dUL, + 0x6dfcc018UL, 0x359fd04eUL, 0x8d23b72bUL, 0x9f9618c5UL, 0x272a7fa0UL, + 0xbafd4719UL, 0x0241207cUL, 0x10f48f92UL, 0xa848e8f7UL, 0x9b14583dUL, + 0x23a83f58UL, 0x311d90b6UL, 0x89a1f7d3UL, 0x1476cf6aUL, 0xaccaa80fUL, + 0xbe7f07e1UL, 0x06c36084UL, 0x5ea070d2UL, 0xe61c17b7UL, 0xf4a9b859UL, + 0x4c15df3cUL, 0xd1c2e785UL, 0x697e80e0UL, 0x7bcb2f0eUL, 0xc377486bUL, + 0xcb0d0fa2UL, 0x73b168c7UL, 0x6104c729UL, 0xd9b8a04cUL, 0x446f98f5UL, + 0xfcd3ff90UL, 0xee66507eUL, 0x56da371bUL, 0x0eb9274dUL, 0xb6054028UL, + 0xa4b0efc6UL, 0x1c0c88a3UL, 0x81dbb01aUL, 0x3967d77fUL, 0x2bd27891UL, + 0x936e1ff4UL, 0x3b26f703UL, 0x839a9066UL, 0x912f3f88UL, 0x299358edUL, + 0xb4446054UL, 0x0cf80731UL, 0x1e4da8dfUL, 0xa6f1cfbaUL, 0xfe92dfecUL, + 0x462eb889UL, 0x549b1767UL, 0xec277002UL, 0x71f048bbUL, 0xc94c2fdeUL, + 0xdbf98030UL, 0x6345e755UL, 0x6b3fa09cUL, 0xd383c7f9UL, 0xc1366817UL, + 0x798a0f72UL, 0xe45d37cbUL, 0x5ce150aeUL, 0x4e54ff40UL, 0xf6e89825UL, + 0xae8b8873UL, 0x1637ef16UL, 0x048240f8UL, 0xbc3e279dUL, 0x21e91f24UL, + 0x99557841UL, 0x8be0d7afUL, 0x335cb0caUL, 0xed59b63bUL, 0x55e5d15eUL, + 0x47507eb0UL, 0xffec19d5UL, 0x623b216cUL, 0xda874609UL, 0xc832e9e7UL, + 0x708e8e82UL, 0x28ed9ed4UL, 0x9051f9b1UL, 0x82e4565fUL, 0x3a58313aUL, + 0xa78f0983UL, 0x1f336ee6UL, 0x0d86c108UL, 0xb53aa66dUL, 0xbd40e1a4UL, + 0x05fc86c1UL, 0x1749292fUL, 0xaff54e4aUL, 0x322276f3UL, 0x8a9e1196UL, + 0x982bbe78UL, 0x2097d91dUL, 0x78f4c94bUL, 0xc048ae2eUL, 0xd2fd01c0UL, + 0x6a4166a5UL, 0xf7965e1cUL, 0x4f2a3979UL, 0x5d9f9697UL, 0xe523f1f2UL, + 0x4d6b1905UL, 0xf5d77e60UL, 0xe762d18eUL, 0x5fdeb6ebUL, 0xc2098e52UL, + 0x7ab5e937UL, 0x680046d9UL, 0xd0bc21bcUL, 0x88df31eaUL, 0x3063568fUL, + 0x22d6f961UL, 0x9a6a9e04UL, 0x07bda6bdUL, 0xbf01c1d8UL, 0xadb46e36UL, + 0x15080953UL, 0x1d724e9aUL, 0xa5ce29ffUL, 0xb77b8611UL, 0x0fc7e174UL, + 0x9210d9cdUL, 0x2aacbea8UL, 0x38191146UL, 0x80a57623UL, 0xd8c66675UL, + 0x607a0110UL, 0x72cfaefeUL, 0xca73c99bUL, 0x57a4f122UL, 0xef189647UL, + 0xfdad39a9UL, 0x45115eccUL, 0x764dee06UL, 0xcef18963UL, 0xdc44268dUL, + 0x64f841e8UL, 0xf92f7951UL, 0x41931e34UL, 0x5326b1daUL, 0xeb9ad6bfUL, + 0xb3f9c6e9UL, 0x0b45a18cUL, 0x19f00e62UL, 0xa14c6907UL, 0x3c9b51beUL, + 0x842736dbUL, 0x96929935UL, 0x2e2efe50UL, 0x2654b999UL, 0x9ee8defcUL, + 0x8c5d7112UL, 0x34e11677UL, 0xa9362eceUL, 0x118a49abUL, 0x033fe645UL, + 0xbb838120UL, 0xe3e09176UL, 0x5b5cf613UL, 0x49e959fdUL, 0xf1553e98UL, + 0x6c820621UL, 0xd43e6144UL, 0xc68bceaaUL, 0x7e37a9cfUL, 0xd67f4138UL, + 0x6ec3265dUL, 0x7c7689b3UL, 0xc4caeed6UL, 0x591dd66fUL, 0xe1a1b10aUL, + 0xf3141ee4UL, 0x4ba87981UL, 0x13cb69d7UL, 0xab770eb2UL, 0xb9c2a15cUL, + 0x017ec639UL, 0x9ca9fe80UL, 0x241599e5UL, 0x36a0360bUL, 0x8e1c516eUL, + 0x866616a7UL, 0x3eda71c2UL, 0x2c6fde2cUL, 0x94d3b949UL, 0x090481f0UL, + 0xb1b8e695UL, 0xa30d497bUL, 0x1bb12e1eUL, 0x43d23e48UL, 0xfb6e592dUL, + 0xe9dbf6c3UL, 0x516791a6UL, 0xccb0a91fUL, 0x740cce7aUL, 0x66b96194UL, + 0xde0506f1UL + }, + { + 0x00000000UL, 0x96300777UL, 0x2c610eeeUL, 0xba510999UL, 0x19c46d07UL, + 0x8ff46a70UL, 0x35a563e9UL, 0xa395649eUL, 0x3288db0eUL, 0xa4b8dc79UL, + 0x1ee9d5e0UL, 0x88d9d297UL, 0x2b4cb609UL, 0xbd7cb17eUL, 0x072db8e7UL, + 0x911dbf90UL, 0x6410b71dUL, 0xf220b06aUL, 0x4871b9f3UL, 0xde41be84UL, + 0x7dd4da1aUL, 0xebe4dd6dUL, 0x51b5d4f4UL, 0xc785d383UL, 0x56986c13UL, + 0xc0a86b64UL, 0x7af962fdUL, 0xecc9658aUL, 0x4f5c0114UL, 0xd96c0663UL, + 0x633d0ffaUL, 0xf50d088dUL, 0xc8206e3bUL, 0x5e10694cUL, 0xe44160d5UL, + 0x727167a2UL, 0xd1e4033cUL, 0x47d4044bUL, 0xfd850dd2UL, 0x6bb50aa5UL, + 0xfaa8b535UL, 0x6c98b242UL, 0xd6c9bbdbUL, 0x40f9bcacUL, 0xe36cd832UL, + 0x755cdf45UL, 0xcf0dd6dcUL, 0x593dd1abUL, 0xac30d926UL, 0x3a00de51UL, + 0x8051d7c8UL, 0x1661d0bfUL, 0xb5f4b421UL, 0x23c4b356UL, 0x9995bacfUL, + 0x0fa5bdb8UL, 0x9eb80228UL, 0x0888055fUL, 0xb2d90cc6UL, 0x24e90bb1UL, + 0x877c6f2fUL, 0x114c6858UL, 0xab1d61c1UL, 0x3d2d66b6UL, 0x9041dc76UL, + 0x0671db01UL, 0xbc20d298UL, 0x2a10d5efUL, 0x8985b171UL, 0x1fb5b606UL, + 0xa5e4bf9fUL, 0x33d4b8e8UL, 0xa2c90778UL, 0x34f9000fUL, 0x8ea80996UL, + 0x18980ee1UL, 0xbb0d6a7fUL, 0x2d3d6d08UL, 0x976c6491UL, 0x015c63e6UL, + 0xf4516b6bUL, 0x62616c1cUL, 0xd8306585UL, 0x4e0062f2UL, 0xed95066cUL, + 0x7ba5011bUL, 0xc1f40882UL, 0x57c40ff5UL, 0xc6d9b065UL, 0x50e9b712UL, + 0xeab8be8bUL, 0x7c88b9fcUL, 0xdf1ddd62UL, 0x492dda15UL, 0xf37cd38cUL, + 0x654cd4fbUL, 0x5861b24dUL, 0xce51b53aUL, 0x7400bca3UL, 0xe230bbd4UL, + 0x41a5df4aUL, 0xd795d83dUL, 0x6dc4d1a4UL, 0xfbf4d6d3UL, 0x6ae96943UL, + 0xfcd96e34UL, 0x468867adUL, 0xd0b860daUL, 0x732d0444UL, 0xe51d0333UL, + 0x5f4c0aaaUL, 0xc97c0dddUL, 0x3c710550UL, 0xaa410227UL, 0x10100bbeUL, + 0x86200cc9UL, 0x25b56857UL, 0xb3856f20UL, 0x09d466b9UL, 0x9fe461ceUL, + 0x0ef9de5eUL, 0x98c9d929UL, 0x2298d0b0UL, 0xb4a8d7c7UL, 0x173db359UL, + 0x810db42eUL, 0x3b5cbdb7UL, 0xad6cbac0UL, 0x2083b8edUL, 0xb6b3bf9aUL, + 0x0ce2b603UL, 0x9ad2b174UL, 0x3947d5eaUL, 0xaf77d29dUL, 0x1526db04UL, + 0x8316dc73UL, 0x120b63e3UL, 0x843b6494UL, 0x3e6a6d0dUL, 0xa85a6a7aUL, + 0x0bcf0ee4UL, 0x9dff0993UL, 0x27ae000aUL, 0xb19e077dUL, 0x44930ff0UL, + 0xd2a30887UL, 0x68f2011eUL, 0xfec20669UL, 0x5d5762f7UL, 0xcb676580UL, + 0x71366c19UL, 0xe7066b6eUL, 0x761bd4feUL, 0xe02bd389UL, 0x5a7ada10UL, + 0xcc4add67UL, 0x6fdfb9f9UL, 0xf9efbe8eUL, 0x43beb717UL, 0xd58eb060UL, + 0xe8a3d6d6UL, 0x7e93d1a1UL, 0xc4c2d838UL, 0x52f2df4fUL, 0xf167bbd1UL, + 0x6757bca6UL, 0xdd06b53fUL, 0x4b36b248UL, 0xda2b0dd8UL, 0x4c1b0aafUL, + 0xf64a0336UL, 0x607a0441UL, 0xc3ef60dfUL, 0x55df67a8UL, 0xef8e6e31UL, + 0x79be6946UL, 0x8cb361cbUL, 0x1a8366bcUL, 0xa0d26f25UL, 0x36e26852UL, + 0x95770cccUL, 0x03470bbbUL, 0xb9160222UL, 0x2f260555UL, 0xbe3bbac5UL, + 0x280bbdb2UL, 0x925ab42bUL, 0x046ab35cUL, 0xa7ffd7c2UL, 0x31cfd0b5UL, + 0x8b9ed92cUL, 0x1daede5bUL, 0xb0c2649bUL, 0x26f263ecUL, 0x9ca36a75UL, + 0x0a936d02UL, 0xa906099cUL, 0x3f360eebUL, 0x85670772UL, 0x13570005UL, + 0x824abf95UL, 0x147ab8e2UL, 0xae2bb17bUL, 0x381bb60cUL, 0x9b8ed292UL, + 0x0dbed5e5UL, 0xb7efdc7cUL, 0x21dfdb0bUL, 0xd4d2d386UL, 0x42e2d4f1UL, + 0xf8b3dd68UL, 0x6e83da1fUL, 0xcd16be81UL, 0x5b26b9f6UL, 0xe177b06fUL, + 0x7747b718UL, 0xe65a0888UL, 0x706a0fffUL, 0xca3b0666UL, 0x5c0b0111UL, + 0xff9e658fUL, 0x69ae62f8UL, 0xd3ff6b61UL, 0x45cf6c16UL, 0x78e20aa0UL, + 0xeed20dd7UL, 0x5483044eUL, 0xc2b30339UL, 0x612667a7UL, 0xf71660d0UL, + 0x4d476949UL, 0xdb776e3eUL, 0x4a6ad1aeUL, 0xdc5ad6d9UL, 0x660bdf40UL, + 0xf03bd837UL, 0x53aebca9UL, 0xc59ebbdeUL, 0x7fcfb247UL, 0xe9ffb530UL, + 0x1cf2bdbdUL, 0x8ac2bacaUL, 0x3093b353UL, 0xa6a3b424UL, 0x0536d0baUL, + 0x9306d7cdUL, 0x2957de54UL, 0xbf67d923UL, 0x2e7a66b3UL, 0xb84a61c4UL, + 0x021b685dUL, 0x942b6f2aUL, 0x37be0bb4UL, 0xa18e0cc3UL, 0x1bdf055aUL, + 0x8def022dUL + }, + { + 0x00000000UL, 0x41311b19UL, 0x82623632UL, 0xc3532d2bUL, 0x04c56c64UL, + 0x45f4777dUL, 0x86a75a56UL, 0xc796414fUL, 0x088ad9c8UL, 0x49bbc2d1UL, + 0x8ae8effaUL, 0xcbd9f4e3UL, 0x0c4fb5acUL, 0x4d7eaeb5UL, 0x8e2d839eUL, + 0xcf1c9887UL, 0x5112c24aUL, 0x1023d953UL, 0xd370f478UL, 0x9241ef61UL, + 0x55d7ae2eUL, 0x14e6b537UL, 0xd7b5981cUL, 0x96848305UL, 0x59981b82UL, + 0x18a9009bUL, 0xdbfa2db0UL, 0x9acb36a9UL, 0x5d5d77e6UL, 0x1c6c6cffUL, + 0xdf3f41d4UL, 0x9e0e5acdUL, 0xa2248495UL, 0xe3159f8cUL, 0x2046b2a7UL, + 0x6177a9beUL, 0xa6e1e8f1UL, 0xe7d0f3e8UL, 0x2483dec3UL, 0x65b2c5daUL, + 0xaaae5d5dUL, 0xeb9f4644UL, 0x28cc6b6fUL, 0x69fd7076UL, 0xae6b3139UL, + 0xef5a2a20UL, 0x2c09070bUL, 0x6d381c12UL, 0xf33646dfUL, 0xb2075dc6UL, + 0x715470edUL, 0x30656bf4UL, 0xf7f32abbUL, 0xb6c231a2UL, 0x75911c89UL, + 0x34a00790UL, 0xfbbc9f17UL, 0xba8d840eUL, 0x79dea925UL, 0x38efb23cUL, + 0xff79f373UL, 0xbe48e86aUL, 0x7d1bc541UL, 0x3c2ade58UL, 0x054f79f0UL, + 0x447e62e9UL, 0x872d4fc2UL, 0xc61c54dbUL, 0x018a1594UL, 0x40bb0e8dUL, + 0x83e823a6UL, 0xc2d938bfUL, 0x0dc5a038UL, 0x4cf4bb21UL, 0x8fa7960aUL, + 0xce968d13UL, 0x0900cc5cUL, 0x4831d745UL, 0x8b62fa6eUL, 0xca53e177UL, + 0x545dbbbaUL, 0x156ca0a3UL, 0xd63f8d88UL, 0x970e9691UL, 0x5098d7deUL, + 0x11a9ccc7UL, 0xd2fae1ecUL, 0x93cbfaf5UL, 0x5cd76272UL, 0x1de6796bUL, + 0xdeb55440UL, 0x9f844f59UL, 0x58120e16UL, 0x1923150fUL, 0xda703824UL, + 0x9b41233dUL, 0xa76bfd65UL, 0xe65ae67cUL, 0x2509cb57UL, 0x6438d04eUL, + 0xa3ae9101UL, 0xe29f8a18UL, 0x21cca733UL, 0x60fdbc2aUL, 0xafe124adUL, + 0xeed03fb4UL, 0x2d83129fUL, 0x6cb20986UL, 0xab2448c9UL, 0xea1553d0UL, + 0x29467efbUL, 0x687765e2UL, 0xf6793f2fUL, 0xb7482436UL, 0x741b091dUL, + 0x352a1204UL, 0xf2bc534bUL, 0xb38d4852UL, 0x70de6579UL, 0x31ef7e60UL, + 0xfef3e6e7UL, 0xbfc2fdfeUL, 0x7c91d0d5UL, 0x3da0cbccUL, 0xfa368a83UL, + 0xbb07919aUL, 0x7854bcb1UL, 0x3965a7a8UL, 0x4b98833bUL, 0x0aa99822UL, + 0xc9fab509UL, 0x88cbae10UL, 0x4f5def5fUL, 0x0e6cf446UL, 0xcd3fd96dUL, + 0x8c0ec274UL, 0x43125af3UL, 0x022341eaUL, 0xc1706cc1UL, 0x804177d8UL, + 0x47d73697UL, 0x06e62d8eUL, 0xc5b500a5UL, 0x84841bbcUL, 0x1a8a4171UL, + 0x5bbb5a68UL, 0x98e87743UL, 0xd9d96c5aUL, 0x1e4f2d15UL, 0x5f7e360cUL, + 0x9c2d1b27UL, 0xdd1c003eUL, 0x120098b9UL, 0x533183a0UL, 0x9062ae8bUL, + 0xd153b592UL, 0x16c5f4ddUL, 0x57f4efc4UL, 0x94a7c2efUL, 0xd596d9f6UL, + 0xe9bc07aeUL, 0xa88d1cb7UL, 0x6bde319cUL, 0x2aef2a85UL, 0xed796bcaUL, + 0xac4870d3UL, 0x6f1b5df8UL, 0x2e2a46e1UL, 0xe136de66UL, 0xa007c57fUL, + 0x6354e854UL, 0x2265f34dUL, 0xe5f3b202UL, 0xa4c2a91bUL, 0x67918430UL, + 0x26a09f29UL, 0xb8aec5e4UL, 0xf99fdefdUL, 0x3accf3d6UL, 0x7bfde8cfUL, + 0xbc6ba980UL, 0xfd5ab299UL, 0x3e099fb2UL, 0x7f3884abUL, 0xb0241c2cUL, + 0xf1150735UL, 0x32462a1eUL, 0x73773107UL, 0xb4e17048UL, 0xf5d06b51UL, + 0x3683467aUL, 0x77b25d63UL, 0x4ed7facbUL, 0x0fe6e1d2UL, 0xccb5ccf9UL, + 0x8d84d7e0UL, 0x4a1296afUL, 0x0b238db6UL, 0xc870a09dUL, 0x8941bb84UL, + 0x465d2303UL, 0x076c381aUL, 0xc43f1531UL, 0x850e0e28UL, 0x42984f67UL, + 0x03a9547eUL, 0xc0fa7955UL, 0x81cb624cUL, 0x1fc53881UL, 0x5ef42398UL, + 0x9da70eb3UL, 0xdc9615aaUL, 0x1b0054e5UL, 0x5a314ffcUL, 0x996262d7UL, + 0xd85379ceUL, 0x174fe149UL, 0x567efa50UL, 0x952dd77bUL, 0xd41ccc62UL, + 0x138a8d2dUL, 0x52bb9634UL, 0x91e8bb1fUL, 0xd0d9a006UL, 0xecf37e5eUL, + 0xadc26547UL, 0x6e91486cUL, 0x2fa05375UL, 0xe836123aUL, 0xa9070923UL, + 0x6a542408UL, 0x2b653f11UL, 0xe479a796UL, 0xa548bc8fUL, 0x661b91a4UL, + 0x272a8abdUL, 0xe0bccbf2UL, 0xa18dd0ebUL, 0x62defdc0UL, 0x23efe6d9UL, + 0xbde1bc14UL, 0xfcd0a70dUL, 0x3f838a26UL, 0x7eb2913fUL, 0xb924d070UL, + 0xf815cb69UL, 0x3b46e642UL, 0x7a77fd5bUL, 0xb56b65dcUL, 0xf45a7ec5UL, + 0x370953eeUL, 0x763848f7UL, 0xb1ae09b8UL, 0xf09f12a1UL, 0x33cc3f8aUL, + 0x72fd2493UL + }, + { + 0x00000000UL, 0x376ac201UL, 0x6ed48403UL, 0x59be4602UL, 0xdca80907UL, + 0xebc2cb06UL, 0xb27c8d04UL, 0x85164f05UL, 0xb851130eUL, 0x8f3bd10fUL, + 0xd685970dUL, 0xe1ef550cUL, 0x64f91a09UL, 0x5393d808UL, 0x0a2d9e0aUL, + 0x3d475c0bUL, 0x70a3261cUL, 0x47c9e41dUL, 0x1e77a21fUL, 0x291d601eUL, + 0xac0b2f1bUL, 0x9b61ed1aUL, 0xc2dfab18UL, 0xf5b56919UL, 0xc8f23512UL, + 0xff98f713UL, 0xa626b111UL, 0x914c7310UL, 0x145a3c15UL, 0x2330fe14UL, + 0x7a8eb816UL, 0x4de47a17UL, 0xe0464d38UL, 0xd72c8f39UL, 0x8e92c93bUL, + 0xb9f80b3aUL, 0x3cee443fUL, 0x0b84863eUL, 0x523ac03cUL, 0x6550023dUL, + 0x58175e36UL, 0x6f7d9c37UL, 0x36c3da35UL, 0x01a91834UL, 0x84bf5731UL, + 0xb3d59530UL, 0xea6bd332UL, 0xdd011133UL, 0x90e56b24UL, 0xa78fa925UL, + 0xfe31ef27UL, 0xc95b2d26UL, 0x4c4d6223UL, 0x7b27a022UL, 0x2299e620UL, + 0x15f32421UL, 0x28b4782aUL, 0x1fdeba2bUL, 0x4660fc29UL, 0x710a3e28UL, + 0xf41c712dUL, 0xc376b32cUL, 0x9ac8f52eUL, 0xada2372fUL, 0xc08d9a70UL, + 0xf7e75871UL, 0xae591e73UL, 0x9933dc72UL, 0x1c259377UL, 0x2b4f5176UL, + 0x72f11774UL, 0x459bd575UL, 0x78dc897eUL, 0x4fb64b7fUL, 0x16080d7dUL, + 0x2162cf7cUL, 0xa4748079UL, 0x931e4278UL, 0xcaa0047aUL, 0xfdcac67bUL, + 0xb02ebc6cUL, 0x87447e6dUL, 0xdefa386fUL, 0xe990fa6eUL, 0x6c86b56bUL, + 0x5bec776aUL, 0x02523168UL, 0x3538f369UL, 0x087faf62UL, 0x3f156d63UL, + 0x66ab2b61UL, 0x51c1e960UL, 0xd4d7a665UL, 0xe3bd6464UL, 0xba032266UL, + 0x8d69e067UL, 0x20cbd748UL, 0x17a11549UL, 0x4e1f534bUL, 0x7975914aUL, + 0xfc63de4fUL, 0xcb091c4eUL, 0x92b75a4cUL, 0xa5dd984dUL, 0x989ac446UL, + 0xaff00647UL, 0xf64e4045UL, 0xc1248244UL, 0x4432cd41UL, 0x73580f40UL, + 0x2ae64942UL, 0x1d8c8b43UL, 0x5068f154UL, 0x67023355UL, 0x3ebc7557UL, + 0x09d6b756UL, 0x8cc0f853UL, 0xbbaa3a52UL, 0xe2147c50UL, 0xd57ebe51UL, + 0xe839e25aUL, 0xdf53205bUL, 0x86ed6659UL, 0xb187a458UL, 0x3491eb5dUL, + 0x03fb295cUL, 0x5a456f5eUL, 0x6d2fad5fUL, 0x801b35e1UL, 0xb771f7e0UL, + 0xeecfb1e2UL, 0xd9a573e3UL, 0x5cb33ce6UL, 0x6bd9fee7UL, 0x3267b8e5UL, + 0x050d7ae4UL, 0x384a26efUL, 0x0f20e4eeUL, 0x569ea2ecUL, 0x61f460edUL, + 0xe4e22fe8UL, 0xd388ede9UL, 0x8a36abebUL, 0xbd5c69eaUL, 0xf0b813fdUL, + 0xc7d2d1fcUL, 0x9e6c97feUL, 0xa90655ffUL, 0x2c101afaUL, 0x1b7ad8fbUL, + 0x42c49ef9UL, 0x75ae5cf8UL, 0x48e900f3UL, 0x7f83c2f2UL, 0x263d84f0UL, + 0x115746f1UL, 0x944109f4UL, 0xa32bcbf5UL, 0xfa958df7UL, 0xcdff4ff6UL, + 0x605d78d9UL, 0x5737bad8UL, 0x0e89fcdaUL, 0x39e33edbUL, 0xbcf571deUL, + 0x8b9fb3dfUL, 0xd221f5ddUL, 0xe54b37dcUL, 0xd80c6bd7UL, 0xef66a9d6UL, + 0xb6d8efd4UL, 0x81b22dd5UL, 0x04a462d0UL, 0x33cea0d1UL, 0x6a70e6d3UL, + 0x5d1a24d2UL, 0x10fe5ec5UL, 0x27949cc4UL, 0x7e2adac6UL, 0x494018c7UL, + 0xcc5657c2UL, 0xfb3c95c3UL, 0xa282d3c1UL, 0x95e811c0UL, 0xa8af4dcbUL, + 0x9fc58fcaUL, 0xc67bc9c8UL, 0xf1110bc9UL, 0x740744ccUL, 0x436d86cdUL, + 0x1ad3c0cfUL, 0x2db902ceUL, 0x4096af91UL, 0x77fc6d90UL, 0x2e422b92UL, + 0x1928e993UL, 0x9c3ea696UL, 0xab546497UL, 0xf2ea2295UL, 0xc580e094UL, + 0xf8c7bc9fUL, 0xcfad7e9eUL, 0x9613389cUL, 0xa179fa9dUL, 0x246fb598UL, + 0x13057799UL, 0x4abb319bUL, 0x7dd1f39aUL, 0x3035898dUL, 0x075f4b8cUL, + 0x5ee10d8eUL, 0x698bcf8fUL, 0xec9d808aUL, 0xdbf7428bUL, 0x82490489UL, + 0xb523c688UL, 0x88649a83UL, 0xbf0e5882UL, 0xe6b01e80UL, 0xd1dadc81UL, + 0x54cc9384UL, 0x63a65185UL, 0x3a181787UL, 0x0d72d586UL, 0xa0d0e2a9UL, + 0x97ba20a8UL, 0xce0466aaUL, 0xf96ea4abUL, 0x7c78ebaeUL, 0x4b1229afUL, + 0x12ac6fadUL, 0x25c6adacUL, 0x1881f1a7UL, 0x2feb33a6UL, 0x765575a4UL, + 0x413fb7a5UL, 0xc429f8a0UL, 0xf3433aa1UL, 0xaafd7ca3UL, 0x9d97bea2UL, + 0xd073c4b5UL, 0xe71906b4UL, 0xbea740b6UL, 0x89cd82b7UL, 0x0cdbcdb2UL, + 0x3bb10fb3UL, 0x620f49b1UL, 0x55658bb0UL, 0x6822d7bbUL, 0x5f4815baUL, + 0x06f653b8UL, 0x319c91b9UL, 0xb48adebcUL, 0x83e01cbdUL, 0xda5e5abfUL, + 0xed3498beUL + }, + { + 0x00000000UL, 0x6567bcb8UL, 0x8bc809aaUL, 0xeeafb512UL, 0x5797628fUL, + 0x32f0de37UL, 0xdc5f6b25UL, 0xb938d79dUL, 0xef28b4c5UL, 0x8a4f087dUL, + 0x64e0bd6fUL, 0x018701d7UL, 0xb8bfd64aUL, 0xddd86af2UL, 0x3377dfe0UL, + 0x56106358UL, 0x9f571950UL, 0xfa30a5e8UL, 0x149f10faUL, 0x71f8ac42UL, + 0xc8c07bdfUL, 0xada7c767UL, 0x43087275UL, 0x266fcecdUL, 0x707fad95UL, + 0x1518112dUL, 0xfbb7a43fUL, 0x9ed01887UL, 0x27e8cf1aUL, 0x428f73a2UL, + 0xac20c6b0UL, 0xc9477a08UL, 0x3eaf32a0UL, 0x5bc88e18UL, 0xb5673b0aUL, + 0xd00087b2UL, 0x6938502fUL, 0x0c5fec97UL, 0xe2f05985UL, 0x8797e53dUL, + 0xd1878665UL, 0xb4e03addUL, 0x5a4f8fcfUL, 0x3f283377UL, 0x8610e4eaUL, + 0xe3775852UL, 0x0dd8ed40UL, 0x68bf51f8UL, 0xa1f82bf0UL, 0xc49f9748UL, + 0x2a30225aUL, 0x4f579ee2UL, 0xf66f497fUL, 0x9308f5c7UL, 0x7da740d5UL, + 0x18c0fc6dUL, 0x4ed09f35UL, 0x2bb7238dUL, 0xc518969fUL, 0xa07f2a27UL, + 0x1947fdbaUL, 0x7c204102UL, 0x928ff410UL, 0xf7e848a8UL, 0x3d58149bUL, + 0x583fa823UL, 0xb6901d31UL, 0xd3f7a189UL, 0x6acf7614UL, 0x0fa8caacUL, + 0xe1077fbeUL, 0x8460c306UL, 0xd270a05eUL, 0xb7171ce6UL, 0x59b8a9f4UL, + 0x3cdf154cUL, 0x85e7c2d1UL, 0xe0807e69UL, 0x0e2fcb7bUL, 0x6b4877c3UL, + 0xa20f0dcbUL, 0xc768b173UL, 0x29c70461UL, 0x4ca0b8d9UL, 0xf5986f44UL, + 0x90ffd3fcUL, 0x7e5066eeUL, 0x1b37da56UL, 0x4d27b90eUL, 0x284005b6UL, + 0xc6efb0a4UL, 0xa3880c1cUL, 0x1ab0db81UL, 0x7fd76739UL, 0x9178d22bUL, + 0xf41f6e93UL, 0x03f7263bUL, 0x66909a83UL, 0x883f2f91UL, 0xed589329UL, + 0x546044b4UL, 0x3107f80cUL, 0xdfa84d1eUL, 0xbacff1a6UL, 0xecdf92feUL, + 0x89b82e46UL, 0x67179b54UL, 0x027027ecUL, 0xbb48f071UL, 0xde2f4cc9UL, + 0x3080f9dbUL, 0x55e74563UL, 0x9ca03f6bUL, 0xf9c783d3UL, 0x176836c1UL, + 0x720f8a79UL, 0xcb375de4UL, 0xae50e15cUL, 0x40ff544eUL, 0x2598e8f6UL, + 0x73888baeUL, 0x16ef3716UL, 0xf8408204UL, 0x9d273ebcUL, 0x241fe921UL, + 0x41785599UL, 0xafd7e08bUL, 0xcab05c33UL, 0x3bb659edUL, 0x5ed1e555UL, + 0xb07e5047UL, 0xd519ecffUL, 0x6c213b62UL, 0x094687daUL, 0xe7e932c8UL, + 0x828e8e70UL, 0xd49eed28UL, 0xb1f95190UL, 0x5f56e482UL, 0x3a31583aUL, + 0x83098fa7UL, 0xe66e331fUL, 0x08c1860dUL, 0x6da63ab5UL, 0xa4e140bdUL, + 0xc186fc05UL, 0x2f294917UL, 0x4a4ef5afUL, 0xf3762232UL, 0x96119e8aUL, + 0x78be2b98UL, 0x1dd99720UL, 0x4bc9f478UL, 0x2eae48c0UL, 0xc001fdd2UL, + 0xa566416aUL, 0x1c5e96f7UL, 0x79392a4fUL, 0x97969f5dUL, 0xf2f123e5UL, + 0x05196b4dUL, 0x607ed7f5UL, 0x8ed162e7UL, 0xebb6de5fUL, 0x528e09c2UL, + 0x37e9b57aUL, 0xd9460068UL, 0xbc21bcd0UL, 0xea31df88UL, 0x8f566330UL, + 0x61f9d622UL, 0x049e6a9aUL, 0xbda6bd07UL, 0xd8c101bfUL, 0x366eb4adUL, + 0x53090815UL, 0x9a4e721dUL, 0xff29cea5UL, 0x11867bb7UL, 0x74e1c70fUL, + 0xcdd91092UL, 0xa8beac2aUL, 0x46111938UL, 0x2376a580UL, 0x7566c6d8UL, + 0x10017a60UL, 0xfeaecf72UL, 0x9bc973caUL, 0x22f1a457UL, 0x479618efUL, + 0xa939adfdUL, 0xcc5e1145UL, 0x06ee4d76UL, 0x6389f1ceUL, 0x8d2644dcUL, + 0xe841f864UL, 0x51792ff9UL, 0x341e9341UL, 0xdab12653UL, 0xbfd69aebUL, + 0xe9c6f9b3UL, 0x8ca1450bUL, 0x620ef019UL, 0x07694ca1UL, 0xbe519b3cUL, + 0xdb362784UL, 0x35999296UL, 0x50fe2e2eUL, 0x99b95426UL, 0xfcdee89eUL, + 0x12715d8cUL, 0x7716e134UL, 0xce2e36a9UL, 0xab498a11UL, 0x45e63f03UL, + 0x208183bbUL, 0x7691e0e3UL, 0x13f65c5bUL, 0xfd59e949UL, 0x983e55f1UL, + 0x2106826cUL, 0x44613ed4UL, 0xaace8bc6UL, 0xcfa9377eUL, 0x38417fd6UL, + 0x5d26c36eUL, 0xb389767cUL, 0xd6eecac4UL, 0x6fd61d59UL, 0x0ab1a1e1UL, + 0xe41e14f3UL, 0x8179a84bUL, 0xd769cb13UL, 0xb20e77abUL, 0x5ca1c2b9UL, + 0x39c67e01UL, 0x80fea99cUL, 0xe5991524UL, 0x0b36a036UL, 0x6e511c8eUL, + 0xa7166686UL, 0xc271da3eUL, 0x2cde6f2cUL, 0x49b9d394UL, 0xf0810409UL, + 0x95e6b8b1UL, 0x7b490da3UL, 0x1e2eb11bUL, 0x483ed243UL, 0x2d596efbUL, + 0xc3f6dbe9UL, 0xa6916751UL, 0x1fa9b0ccUL, 0x7ace0c74UL, 0x9461b966UL, + 0xf10605deUL +#endif + } +}; diff --git a/src/lib/zlib/deflate.c b/src/lib/zlib/deflate.c new file mode 100644 index 00000000000..29ce1f64a57 --- /dev/null +++ b/src/lib/zlib/deflate.c @@ -0,0 +1,1736 @@ +/* deflate.c -- compress data using the deflation algorithm + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process depends on being able to identify portions + * of the input text which are identical to earlier input (within a + * sliding window trailing behind the input currently being processed). + * + * The most straightforward technique turns out to be the fastest for + * most input files: try all possible matches and select the longest. + * The key feature of this algorithm is that insertions into the string + * dictionary are very simple and thus fast, and deletions are avoided + * completely. Insertions are performed at each input character, whereas + * string matches are performed only when the previous match ends. So it + * is preferable to spend more time in matches to allow very fast string + * insertions and avoid deletions. The matching algorithm for small + * strings is inspired from that of Rabin & Karp. A brute force approach + * is used to find longer strings when a small match has been found. + * A similar algorithm is used in comic (by Jan-Mark Wams) and freeze + * (by Leonid Broukhis). + * A previous version of this file used a more sophisticated algorithm + * (by Fiala and Greene) which is guaranteed to run in linear amortized + * time, but has a larger average cost, uses more memory and is patented. + * However the F&G algorithm may be faster for some highly redundant + * files if the parameter max_chain_length (described below) is too large. + * + * ACKNOWLEDGEMENTS + * + * The idea of lazy evaluation of matches is due to Jan-Mark Wams, and + * I found it in 'freeze' written by Leonid Broukhis. + * Thanks to many people for bug reports and testing. + * + * REFERENCES + * + * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". + * Available in http://www.ietf.org/rfc/rfc1951.txt + * + * A description of the Rabin and Karp algorithm is given in the book + * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. + * + * Fiala,E.R., and Greene,D.H. + * Data Compression with Finite Windows, Comm.ACM, 32,4 (1989) 490-595 + * + */ + +/* @(#) $Id$ */ + +#include "deflate.h" + +const char deflate_copyright[] = + " deflate 1.2.3 Copyright 1995-2005 Jean-loup Gailly "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* =========================================================================== + * Function prototypes. + */ +typedef enum { + need_more, /* block not completed, need more input or more output */ + block_done, /* block flush performed */ + finish_started, /* finish started, need only more output at next deflate */ + finish_done /* finish done, accept no more input or output */ +} block_state; + +typedef block_state (*compress_func) OF((deflate_state *s, int flush)); +/* Compression function. Returns the block state after the call. */ + +local void fill_window OF((deflate_state *s)); +local block_state deflate_stored OF((deflate_state *s, int flush)); +local block_state deflate_fast OF((deflate_state *s, int flush)); +#ifndef FASTEST +local block_state deflate_slow OF((deflate_state *s, int flush)); +#endif +local void lm_init OF((deflate_state *s)); +local void putShortMSB OF((deflate_state *s, uInt b)); +local void flush_pending OF((z_streamp strm)); +local int read_buf OF((z_streamp strm, Bytef *buf, unsigned size)); +#ifndef FASTEST +#ifdef ASMV + void match_init OF((void)); /* asm code initialization */ + uInt longest_match OF((deflate_state *s, IPos cur_match)); +#else +local uInt longest_match OF((deflate_state *s, IPos cur_match)); +#endif +#endif +local uInt longest_match_fast OF((deflate_state *s, IPos cur_match)); + +#ifdef DEBUG +local void check_match OF((deflate_state *s, IPos start, IPos match, + int length)); +#endif + +/* =========================================================================== + * Local data + */ + +#define NIL 0 +/* Tail of hash chains */ + +#ifndef TOO_FAR +# define TOO_FAR 4096 +#endif +/* Matches of length 3 are discarded if their distance exceeds TOO_FAR */ + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +typedef struct config_s { + ush good_length; /* reduce lazy search above this match length */ + ush max_lazy; /* do not perform lazy search above this match length */ + ush nice_length; /* quit search above this match length */ + ush max_chain; + compress_func func; +} config; + +#ifdef FASTEST +local const config configuration_table[2] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}}; /* max speed, no lazy matches */ +#else +local const config configuration_table[10] = { +/* good lazy nice chain */ +/* 0 */ {0, 0, 0, 0, deflate_stored}, /* store only */ +/* 1 */ {4, 4, 8, 4, deflate_fast}, /* max speed, no lazy matches */ +/* 2 */ {4, 5, 16, 8, deflate_fast}, +/* 3 */ {4, 6, 32, 32, deflate_fast}, + +/* 4 */ {4, 4, 16, 16, deflate_slow}, /* lazy matches */ +/* 5 */ {8, 16, 32, 32, deflate_slow}, +/* 6 */ {8, 16, 128, 128, deflate_slow}, +/* 7 */ {8, 32, 128, 256, deflate_slow}, +/* 8 */ {32, 128, 258, 1024, deflate_slow}, +/* 9 */ {32, 258, 258, 4096, deflate_slow}}; /* max compression */ +#endif + +/* Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 + * For deflate_fast() (levels <= 3) good is ignored and lazy has a different + * meaning. + */ + +#define EQUAL 0 +/* result of memcmp for equal strings */ + +#ifndef NO_DUMMY_DECL +struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ +#endif + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(s,h,c) (h = (((h)<<s->hash_shift) ^ (c)) & s->hash_mask) + + +/* =========================================================================== + * Insert string str in the dictionary and set match_head to the previous head + * of the hash chain (the most recent string with same hash key). Return + * the previous length of the hash chain. + * If this file is compiled with -DFASTEST, the compression level is forced + * to 1, and no hash chains are maintained. + * IN assertion: all calls to to INSERT_STRING are made with consecutive + * input characters and the first MIN_MATCH bytes of str are valid + * (except for the last MIN_MATCH-1 bytes of the input file). + */ +#ifdef FASTEST +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#else +#define INSERT_STRING(s, str, match_head) \ + (UPDATE_HASH(s, s->ins_h, s->window[(str) + (MIN_MATCH-1)]), \ + match_head = s->prev[(str) & s->w_mask] = s->head[s->ins_h], \ + s->head[s->ins_h] = (Pos)(str)) +#endif + +/* =========================================================================== + * Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ +#define CLEAR_HASH(s) \ + s->head[s->hash_size-1] = NIL; \ + zmemzero((Bytef *)s->head, (unsigned)(s->hash_size-1)*sizeof(*s->head)); + +/* ========================================================================= */ +int ZEXPORT deflateInit_(strm, level, version, stream_size) + z_streamp strm; + int level; + const char *version; + int stream_size; +{ + return deflateInit2_(strm, level, Z_DEFLATED, MAX_WBITS, DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY, version, stream_size); + /* To do: ignore strm->next_in if we use it as window */ +} + +/* ========================================================================= */ +int ZEXPORT deflateInit2_(strm, level, method, windowBits, memLevel, strategy, + version, stream_size) + z_streamp strm; + int level; + int method; + int windowBits; + int memLevel; + int strategy; + const char *version; + int stream_size; +{ + deflate_state *s; + int wrap = 1; + static const char my_version[] = ZLIB_VERSION; + + ushf *overlay; + /* We overlay pending_buf and d_buf+l_buf. This works since the average + * output size for (length,distance) codes is <= 24 bits. + */ + + if (version == Z_NULL || version[0] != my_version[0] || + stream_size != sizeof(z_stream)) { + return Z_VERSION_ERROR; + } + if (strm == Z_NULL) return Z_STREAM_ERROR; + + strm->msg = Z_NULL; + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + + if (windowBits < 0) { /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } +#ifdef GZIP + else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } +#endif + if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method != Z_DEFLATED || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + if (windowBits == 8) windowBits = 9; /* until 256-byte window bug fixed */ + s = (deflate_state *) ZALLOC(strm, 1, sizeof(deflate_state)); + if (s == Z_NULL) return Z_MEM_ERROR; + strm->state = (struct internal_state FAR *)s; + s->strm = strm; + + s->wrap = wrap; + s->gzhead = Z_NULL; + s->w_bits = windowBits; + s->w_size = 1 << s->w_bits; + s->w_mask = s->w_size - 1; + + s->hash_bits = memLevel + 7; + s->hash_size = 1 << s->hash_bits; + s->hash_mask = s->hash_size - 1; + s->hash_shift = ((s->hash_bits+MIN_MATCH-1)/MIN_MATCH); + + s->window = (Bytef *) ZALLOC(strm, s->w_size, 2*sizeof(Byte)); + s->prev = (Posf *) ZALLOC(strm, s->w_size, sizeof(Pos)); + s->head = (Posf *) ZALLOC(strm, s->hash_size, sizeof(Pos)); + + s->lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + s->pending_buf = (uchf *) overlay; + s->pending_buf_size = (ulg)s->lit_bufsize * (sizeof(ush)+2L); + + if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || + s->pending_buf == Z_NULL) { + s->status = FINISH_STATE; + strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); + deflateEnd (strm); + return Z_MEM_ERROR; + } + s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + + s->level = level; + s->strategy = strategy; + s->method = (Byte)method; + + return deflateReset(strm); +} + +/* ========================================================================= */ +int ZEXPORT deflateSetDictionary (strm, dictionary, dictLength) + z_streamp strm; + const Bytef *dictionary; + uInt dictLength; +{ + deflate_state *s; + uInt length = dictLength; + uInt n; + IPos hash_head = 0; + + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL || + strm->state->wrap == 2 || + (strm->state->wrap == 1 && strm->state->status != INIT_STATE)) + return Z_STREAM_ERROR; + + s = strm->state; + if (s->wrap) + strm->adler = adler32(strm->adler, dictionary, dictLength); + + if (length < MIN_MATCH) return Z_OK; + if (length > MAX_DIST(s)) { + length = MAX_DIST(s); + dictionary += dictLength - length; /* use the tail of the dictionary */ + } + zmemcpy(s->window, dictionary, length); + s->strstart = length; + s->block_start = (long)length; + + /* Insert all strings in the hash table (except for the last two bytes). + * s->lookahead stays null, so s->ins_h will be recomputed at the next + * call of fill_window. + */ + s->ins_h = s->window[0]; + UPDATE_HASH(s, s->ins_h, s->window[1]); + for (n = 0; n <= length - MIN_MATCH; n++) { + INSERT_STRING(s, n, hash_head); + } + if (hash_head) hash_head = 0; /* to make compiler happy */ + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateReset (strm) + z_streamp strm; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + strm->zalloc == (alloc_func)0 || strm->zfree == (free_func)0) { + return Z_STREAM_ERROR; + } + + strm->total_in = strm->total_out = 0; + strm->msg = Z_NULL; /* use zfree if we ever allocate msg dynamically */ + strm->data_type = Z_UNKNOWN; + + s = (deflate_state *)strm->state; + s->pending = 0; + s->pending_out = s->pending_buf; + + if (s->wrap < 0) { + s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ + } + s->status = s->wrap ? INIT_STATE : BUSY_STATE; + strm->adler = +#ifdef GZIP + s->wrap == 2 ? crc32(0L, Z_NULL, 0) : +#endif + adler32(0L, Z_NULL, 0); + s->last_flush = Z_NO_FLUSH; + + _tr_init(s); + lm_init(s); + + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateSetHeader (strm, head) + z_streamp strm; + gz_headerp head; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (strm->state->wrap != 2) return Z_STREAM_ERROR; + strm->state->gzhead = head; + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflatePrime (strm, bits, value) + z_streamp strm; + int bits; + int value; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + strm->state->bi_valid = bits; + strm->state->bi_buf = (ush)(value & ((1 << bits) - 1)); + return Z_OK; +} + +/* ========================================================================= */ +int ZEXPORT deflateParams(strm, level, strategy) + z_streamp strm; + int level; + int strategy; +{ + deflate_state *s; + compress_func func; + int err = Z_OK; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + +#ifdef FASTEST + if (level != 0) level = 1; +#else + if (level == Z_DEFAULT_COMPRESSION) level = 6; +#endif + if (level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED) { + return Z_STREAM_ERROR; + } + func = configuration_table[s->level].func; + + if (func != configuration_table[level].func && strm->total_in != 0) { + /* Flush the last buffer: */ + err = deflate(strm, Z_PARTIAL_FLUSH); + } + if (s->level != level) { + s->level = level; + s->max_lazy_match = configuration_table[level].max_lazy; + s->good_match = configuration_table[level].good_length; + s->nice_match = configuration_table[level].nice_length; + s->max_chain_length = configuration_table[level].max_chain; + } + s->strategy = strategy; + return err; +} + +/* ========================================================================= */ +int ZEXPORT deflateTune(strm, good_length, max_lazy, nice_length, max_chain) + z_streamp strm; + int good_length; + int max_lazy; + int nice_length; + int max_chain; +{ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + s = strm->state; + s->good_match = good_length; + s->max_lazy_match = max_lazy; + s->nice_match = nice_length; + s->max_chain_length = max_chain; + return Z_OK; +} + +/* ========================================================================= + * For the default windowBits of 15 and memLevel of 8, this function returns + * a close to exact, as well as small, upper bound on the compressed size. + * They are coded as constants here for a reason--if the #define's are + * changed, then this function needs to be changed as well. The return + * value for 15 and 8 only works for those exact settings. + * + * For any setting other than those defaults for windowBits and memLevel, + * the value returned is a conservative worst case for the maximum expansion + * resulting from using fixed blocks instead of stored blocks, which deflate + * can emit on compressed data for some combinations of the parameters. + * + * This function could be more sophisticated to provide closer upper bounds + * for every combination of windowBits and memLevel, as well as wrap. + * But even the conservative upper bound of about 14% expansion does not + * seem onerous for output buffer allocation. + */ +uLong ZEXPORT deflateBound(strm, sourceLen) + z_streamp strm; + uLong sourceLen; +{ + deflate_state *s; + uLong destLen; + + /* conservative upper bound */ + destLen = sourceLen + + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 11; + + /* if can't get parameters, return conservative bound */ + if (strm == Z_NULL || strm->state == Z_NULL) + return destLen; + + /* if not default parameters, return conservative bound */ + s = strm->state; + if (s->w_bits != 15 || s->hash_bits != 8 + 7) + return destLen; + + /* default settings: return tight bound for that case */ + return compressBound(sourceLen); +} + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +local void putShortMSB (s, b) + deflate_state *s; + uInt b; +{ + put_byte(s, (Byte)(b >> 8)); + put_byte(s, (Byte)(b & 0xff)); +} + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->next_out buffer and copying into it. + * (See also read_buf()). + */ +local void flush_pending(strm) + z_streamp strm; +{ + unsigned len = strm->state->pending; + + if (len > strm->avail_out) len = strm->avail_out; + if (len == 0) return; + + zmemcpy(strm->next_out, strm->state->pending_out, len); + strm->next_out += len; + strm->state->pending_out += len; + strm->total_out += len; + strm->avail_out -= len; + strm->state->pending -= len; + if (strm->state->pending == 0) { + strm->state->pending_out = strm->state->pending_buf; + } +} + +/* ========================================================================= */ +int ZEXPORT deflate (strm, flush) + z_streamp strm; + int flush; +{ + int old_flush; /* value of flush param for previous deflate call */ + deflate_state *s; + + if (strm == Z_NULL || strm->state == Z_NULL || + flush > Z_FINISH || flush < 0) { + return Z_STREAM_ERROR; + } + s = strm->state; + + if (strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0) || + (s->status == FINISH_STATE && flush != Z_FINISH)) { + ERR_RETURN(strm, Z_STREAM_ERROR); + } + if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); + + s->strm = strm; /* just in case */ + old_flush = s->last_flush; + s->last_flush = flush; + + /* Write the header */ + if (s->status == INIT_STATE) { +#ifdef GZIP + if (s->wrap == 2) { + strm->adler = crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (s->gzhead == NULL) { + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, OS_CODE); + s->status = BUSY_STATE; + } + else { + put_byte(s, (s->gzhead->text ? 1 : 0) + + (s->gzhead->hcrc ? 2 : 0) + + (s->gzhead->extra == Z_NULL ? 0 : 4) + + (s->gzhead->name == Z_NULL ? 0 : 8) + + (s->gzhead->comment == Z_NULL ? 0 : 16) + ); + put_byte(s, (Byte)(s->gzhead->time & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff)); + put_byte(s, (Byte)((s->gzhead->time >> 24) & 0xff)); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? + 4 : 0)); + put_byte(s, s->gzhead->os & 0xff); + if (s->gzhead->extra != NULL) { + put_byte(s, s->gzhead->extra_len & 0xff); + put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); + } + if (s->gzhead->hcrc) + strm->adler = crc32(strm->adler, s->pending_buf, + s->pending); + s->gzindex = 0; + s->status = EXTRA_STATE; + } + } + else +#endif + { + uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + uInt level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; + else + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s->status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + strm->adler = adler32(0L, Z_NULL, 0); + } + } +#ifdef GZIP + if (s->status == EXTRA_STATE) { + if (s->gzhead->extra != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + + while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) + break; + } + put_byte(s, s->gzhead->extra[s->gzindex]); + s->gzindex++; + } + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (s->gzindex == s->gzhead->extra_len) { + s->gzindex = 0; + s->status = NAME_STATE; + } + } + else + s->status = NAME_STATE; + } + if (s->status == NAME_STATE) { + if (s->gzhead->name != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->name[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) { + s->gzindex = 0; + s->status = COMMENT_STATE; + } + } + else + s->status = COMMENT_STATE; + } + if (s->status == COMMENT_STATE) { + if (s->gzhead->comment != NULL) { + uInt beg = s->pending; /* start of bytes to update crc */ + int val; + + do { + if (s->pending == s->pending_buf_size) { + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + flush_pending(strm); + beg = s->pending; + if (s->pending == s->pending_buf_size) { + val = 1; + break; + } + } + val = s->gzhead->comment[s->gzindex++]; + put_byte(s, val); + } while (val != 0); + if (s->gzhead->hcrc && s->pending > beg) + strm->adler = crc32(strm->adler, s->pending_buf + beg, + s->pending - beg); + if (val == 0) + s->status = HCRC_STATE; + } + else + s->status = HCRC_STATE; + } + if (s->status == HCRC_STATE) { + if (s->gzhead->hcrc) { + if (s->pending + 2 > s->pending_buf_size) + flush_pending(strm); + if (s->pending + 2 <= s->pending_buf_size) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + strm->adler = crc32(0L, Z_NULL, 0); + s->status = BUSY_STATE; + } + } + else + s->status = BUSY_STATE; + } +#endif + + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm->avail_in == 0 && flush <= old_flush && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* Start a new block or continue the current one. + */ + if (strm->avail_in != 0 || s->lookahead != 0 || + (flush != Z_NO_FLUSH && s->status != FINISH_STATE)) { + block_state bstate; + + bstate = (*(configuration_table[s->level].func))(s, flush); + + if (bstate == finish_started || bstate == finish_done) { + s->status = FINISH_STATE; + } + if (bstate == need_more || bstate == finish_started) { + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR next call, see above */ + } + return Z_OK; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate == block_done) { + if (flush == Z_PARTIAL_FLUSH) { + _tr_align(s); + } else { /* FULL_FLUSH or SYNC_FLUSH */ + _tr_stored_block(s, (char*)0, 0L, 0); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush == Z_FULL_FLUSH) { + CLEAR_HASH(s); /* forget history */ + } + } + flush_pending(strm); + if (strm->avail_out == 0) { + s->last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK; + } + } + } + Assert(strm->avail_out > 0, "bug2"); + + if (flush != Z_FINISH) return Z_OK; + if (s->wrap <= 0) return Z_STREAM_END; + + /* Write the trailer */ +#ifdef GZIP + if (s->wrap == 2) { + put_byte(s, (Byte)(strm->adler & 0xff)); + put_byte(s, (Byte)((strm->adler >> 8) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 16) & 0xff)); + put_byte(s, (Byte)((strm->adler >> 24) & 0xff)); + put_byte(s, (Byte)(strm->total_in & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 8) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 16) & 0xff)); + put_byte(s, (Byte)((strm->total_in >> 24) & 0xff)); + } + else +#endif + { + putShortMSB(s, (uInt)(strm->adler >> 16)); + putShortMSB(s, (uInt)(strm->adler & 0xffff)); + } + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s->wrap > 0) s->wrap = -s->wrap; /* write the trailer only once! */ + return s->pending != 0 ? Z_OK : Z_STREAM_END; +} + +/* ========================================================================= */ +int ZEXPORT deflateEnd (strm) + z_streamp strm; +{ + int status; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + + status = strm->state->status; + if (status != INIT_STATE && + status != EXTRA_STATE && + status != NAME_STATE && + status != COMMENT_STATE && + status != HCRC_STATE && + status != BUSY_STATE && + status != FINISH_STATE) { + return Z_STREAM_ERROR; + } + + /* Deallocate in reverse order of allocations: */ + TRY_FREE(strm, strm->state->pending_buf); + TRY_FREE(strm, strm->state->head); + TRY_FREE(strm, strm->state->prev); + TRY_FREE(strm, strm->state->window); + + ZFREE(strm, strm->state); + strm->state = Z_NULL; + + return status == BUSY_STATE ? Z_DATA_ERROR : Z_OK; +} + +/* ========================================================================= + * Copy the source state to the destination state. + * To simplify the source, this is not supported for 16-bit MSDOS (which + * doesn't have enough memory anyway to duplicate compression states). + */ +int ZEXPORT deflateCopy (dest, source) + z_streamp dest; + z_streamp source; +{ +#ifdef MAXSEG_64K + return Z_STREAM_ERROR; +#else + deflate_state *ds; + deflate_state *ss; + ushf *overlay; + + + if (source == Z_NULL || dest == Z_NULL || source->state == Z_NULL) { + return Z_STREAM_ERROR; + } + + ss = source->state; + + zmemcpy(dest, source, sizeof(z_stream)); + + ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); + if (ds == Z_NULL) return Z_MEM_ERROR; + dest->state = (struct internal_state FAR *) ds; + zmemcpy(ds, ss, sizeof(deflate_state)); + ds->strm = dest; + + ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); + ds->prev = (Posf *) ZALLOC(dest, ds->w_size, sizeof(Pos)); + ds->head = (Posf *) ZALLOC(dest, ds->hash_size, sizeof(Pos)); + overlay = (ushf *) ZALLOC(dest, ds->lit_bufsize, sizeof(ush)+2); + ds->pending_buf = (uchf *) overlay; + + if (ds->window == Z_NULL || ds->prev == Z_NULL || ds->head == Z_NULL || + ds->pending_buf == Z_NULL) { + deflateEnd (dest); + return Z_MEM_ERROR; + } + /* following zmemcpy do not work for 16-bit MSDOS */ + zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); + zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); + + ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); + ds->d_buf = overlay + ds->lit_bufsize/sizeof(ush); + ds->l_buf = ds->pending_buf + (1+sizeof(ush))*ds->lit_bufsize; + + ds->l_desc.dyn_tree = ds->dyn_ltree; + ds->d_desc.dyn_tree = ds->dyn_dtree; + ds->bl_desc.dyn_tree = ds->bl_tree; + + return Z_OK; +#endif /* MAXSEG_64K */ +} + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->next_in buffer and copying from it. + * (See also flush_pending()). + */ +local int read_buf(strm, buf, size) + z_streamp strm; + Bytef *buf; + unsigned size; +{ + unsigned len = strm->avail_in; + + if (len > size) len = size; + if (len == 0) return 0; + + strm->avail_in -= len; + + if (strm->state->wrap == 1) { + strm->adler = adler32(strm->adler, strm->next_in, len); + } +#ifdef GZIP + else if (strm->state->wrap == 2) { + strm->adler = crc32(strm->adler, strm->next_in, len); + } +#endif + zmemcpy(buf, strm->next_in, len); + strm->next_in += len; + strm->total_in += len; + + return (int)len; +} + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +local void lm_init (s) + deflate_state *s; +{ + s->window_size = (ulg)2L*s->w_size; + + CLEAR_HASH(s); + + /* Set the default configuration parameters: + */ + s->max_lazy_match = configuration_table[s->level].max_lazy; + s->good_match = configuration_table[s->level].good_length; + s->nice_match = configuration_table[s->level].nice_length; + s->max_chain_length = configuration_table[s->level].max_chain; + + s->strstart = 0; + s->block_start = 0L; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + s->ins_h = 0; +#ifndef FASTEST +#ifdef ASMV + match_init(); /* initialize the asm code */ +#endif +#endif +} + +#ifndef FASTEST +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +#ifndef ASMV +/* For 80x86 and 680x0, an optimized version will be provided in match.asm or + * match.S. The code will be functionally equivalent. + */ +local uInt longest_match(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + unsigned chain_length = s->max_chain_length;/* max hash chain length */ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + int best_len = s->prev_length; /* best match length so far */ + int nice_match = s->nice_match; /* stop if match long enough */ + IPos limit = s->strstart > (IPos)MAX_DIST(s) ? + s->strstart - (IPos)MAX_DIST(s) : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + Posf *prev = s->prev; + uInt wmask = s->w_mask; + +#ifdef UNALIGNED_OK + /* Compare two bytes at a time. Note: this is not always beneficial. + * Try with and without -DUNALIGNED_OK to check. + */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH - 1; + register ush scan_start = *(ushf*)scan; + register ush scan_end = *(ushf*)(scan+best_len-1); +#else + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + register Byte scan_end1 = scan[best_len-1]; + register Byte scan_end = scan[best_len]; +#endif + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s->prev_length >= s->good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if ((uInt)nice_match > s->lookahead) nice_match = s->lookahead; + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + Assert(cur_match < s->strstart, "no future"); + match = s->window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ +#if (defined(UNALIGNED_OK) && MAX_MATCH == 258) + /* This code assumes sizeof(unsigned short) == 2. Do not use + * UNALIGNED_OK if your compiler uses a different size. + */ + if (*(ushf*)(match+best_len-1) != scan_end || + *(ushf*)match != scan_start) continue; + + /* It is not necessary to compare scan[2] and match[2] since they are + * always equal when the other bytes match, given that the hash keys + * are equal and that HASH_BITS >= 8. Compare 2 bytes at a time at + * strstart+3, +5, ... up to strstart+257. We check for insufficient + * lookahead only every 4th comparison; the 128th check will be made + * at strstart+257. If MAX_MATCH-2 is not a multiple of 8, it is + * necessary to put more guard bytes at the end of the window, or + * to check more often for insufficient lookahead. + */ + Assert(scan[2] == match[2], "scan[2]?"); + scan++, match++; + do { + } while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + *(ushf*)(scan+=2) == *(ushf*)(match+=2) && + scan < strend); + /* The funny "do {}" generates better code on most compilers */ + + /* Here, scan <= window+strstart+257 */ + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + if (*scan == *match) scan++; + + len = (MAX_MATCH - 1) - (int)(strend-scan); + scan = strend - (MAX_MATCH-1); + +#else /* UNALIGNED_OK */ + + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + +#endif /* UNALIGNED_OK */ + + if (len > best_len) { + s->match_start = cur_match; + best_len = len; + if (len >= nice_match) break; +#ifdef UNALIGNED_OK + scan_end = *(ushf*)(scan+best_len-1); +#else + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; +#endif + } + } while ((cur_match = prev[cur_match & wmask]) > limit + && --chain_length != 0); + + if ((uInt)best_len <= s->lookahead) return (uInt)best_len; + return s->lookahead; +} +#endif /* ASMV */ +#endif /* FASTEST */ + +/* --------------------------------------------------------------------------- + * Optimized version for level == 1 or strategy == Z_RLE only + */ +local uInt longest_match_fast(s, cur_match) + deflate_state *s; + IPos cur_match; /* current match */ +{ + register Bytef *scan = s->window + s->strstart; /* current string */ + register Bytef *match; /* matched string */ + register int len; /* length of current match */ + register Bytef *strend = s->window + s->strstart + MAX_MATCH; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + Assert(cur_match < s->strstart, "no future"); + + match = s->window + cur_match; + + /* Return failure if the match length is less than 2: + */ + if (match[0] != scan[0] || match[1] != scan[1]) return MIN_MATCH-1; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match += 2; + Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + + if (len < MIN_MATCH) return MIN_MATCH - 1; + + s->match_start = cur_match; + return (uInt)len <= s->lookahead ? (uInt)len : s->lookahead; +} + +#ifdef DEBUG +/* =========================================================================== + * Check that the match at match_start is indeed a match. + */ +local void check_match(s, start, match, length) + deflate_state *s; + IPos start, match; + int length; +{ + /* check that the match is indeed a match */ + if (zmemcmp(s->window + match, + s->window + start, length) != EQUAL) { + fprintf(stderr, " start %u, match %u, length %d\n", + start, match, length); + do { + fprintf(stderr, "%c%c", s->window[match++], s->window[start++]); + } while (--length != 0); + z_error("invalid match"); + } + if (z_verbose > 1) { + fprintf(stderr,"\\[%d,%d]", start-match, length); + do { putc(s->window[start++], stderr); } while (--length != 0); + } +} +#else +# define check_match(s, start, match, length) +#endif /* DEBUG */ + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +local void fill_window(s) + deflate_state *s; +{ + register unsigned n, m; + register Posf *p; + unsigned more; /* Amount of free space at the end of the window. */ + uInt wsize = s->w_size; + + do { + more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); + + /* Deal with !@#$% 64K limit: */ + if (sizeof(int) <= 2) { + if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + more = wsize; + + } else if (more == (unsigned)(-1)) { + /* Very unlikely, but possible on 16 bit machine if + * strstart == 0 && lookahead == 1 (input done a byte at time) + */ + more--; + } + } + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s->strstart >= wsize+MAX_DIST(s)) { + + zmemcpy(s->window, s->window+wsize, (unsigned)wsize); + s->match_start -= wsize; + s->strstart -= wsize; /* we now have strstart >= MAX_DIST */ + s->block_start -= (long) wsize; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + /* %%% avoid this when Z_RLE */ + n = s->hash_size; + p = &s->head[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + } while (--n); + + n = wsize; +#ifndef FASTEST + p = &s->prev[n]; + do { + m = *--p; + *p = (Pos)(m >= wsize ? m-wsize : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); +#endif + more += wsize; + } + if (s->strm->avail_in == 0) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(more >= 2, "more < 2"); + + n = read_buf(s->strm, s->window + s->strstart + s->lookahead, more); + s->lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s->lookahead >= MIN_MATCH) { + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + + } while (s->lookahead < MIN_LOOKAHEAD && s->strm->avail_in != 0); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK_ONLY(s, eof) { \ + _tr_flush_block(s, (s->block_start >= 0L ? \ + (charf *)&s->window[(unsigned)s->block_start] : \ + (charf *)Z_NULL), \ + (ulg)((long)s->strstart - s->block_start), \ + (eof)); \ + s->block_start = s->strstart; \ + flush_pending(s->strm); \ + Tracev((stderr,"[FLUSH]")); \ +} + +/* Same but force premature exit if necessary. */ +#define FLUSH_BLOCK(s, eof) { \ + FLUSH_BLOCK_ONLY(s, eof); \ + if (s->strm->avail_out == 0) return (eof) ? finish_started : need_more; \ +} + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +local block_state deflate_stored(s, flush) + deflate_state *s; + int flush; +{ + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + ulg max_block_size = 0xffff; + ulg max_start; + + if (max_block_size > s->pending_buf_size - 5) { + max_block_size = s->pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s->lookahead <= 1) { + + Assert(s->strstart < s->w_size+MAX_DIST(s) || + s->block_start >= (long)s->w_size, "slide too late"); + + fill_window(s); + if (s->lookahead == 0 && flush == Z_NO_FLUSH) return need_more; + + if (s->lookahead == 0) break; /* flush the current block */ + } + Assert(s->block_start >= 0L, "block gone"); + + s->strstart += s->lookahead; + s->lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + max_start = s->block_start + max_block_size; + if (s->strstart == 0 || (ulg)s->strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s->lookahead = (uInt)(s->strstart - max_start); + s->strstart = (uInt)max_start; + FLUSH_BLOCK(s, 0); + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s->strstart - (uInt)s->block_start >= MAX_DIST(s)) { + FLUSH_BLOCK(s, 0); + } + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +local block_state deflate_fast(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of the hash chain */ + int bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ +#ifdef FASTEST + if ((s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) || + (s->strategy == Z_RLE && s->strstart - hash_head == 1)) { + s->match_length = longest_match_fast (s, hash_head); + } +#else + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } +#endif + /* longest_match() or longest_match_fast() sets match_start */ + } + if (s->match_length >= MIN_MATCH) { + check_match(s, s->strstart, s->match_start, s->match_length); + + _tr_tally_dist(s, s->strstart - s->match_start, + s->match_length - MIN_MATCH, bflush); + + s->lookahead -= s->match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ +#ifndef FASTEST + if (s->match_length <= s->max_insert_length && + s->lookahead >= MIN_MATCH) { + s->match_length--; /* string at strstart already in table */ + do { + s->strstart++; + INSERT_STRING(s, s->strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s->match_length != 0); + s->strstart++; + } else +#endif + { + s->strstart += s->match_length; + s->match_length = 0; + s->ins_h = s->window[s->strstart]; + UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); +#if MIN_MATCH != 3 + Call UPDATE_HASH() MIN_MATCH-3 more times +#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} + +#ifndef FASTEST +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +local block_state deflate_slow(s, flush) + deflate_state *s; + int flush; +{ + IPos hash_head = NIL; /* head of hash chain */ + int bflush; /* set if current block must be flushed */ + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s->lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s->lookahead < MIN_LOOKAHEAD && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + if (s->lookahead >= MIN_MATCH) { + INSERT_STRING(s, s->strstart, hash_head); + } + + /* Find the longest match, discarding those <= prev_length. + */ + s->prev_length = s->match_length, s->prev_match = s->match_start; + s->match_length = MIN_MATCH-1; + + if (hash_head != NIL && s->prev_length < s->max_lazy_match && + s->strstart - hash_head <= MAX_DIST(s)) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + if (s->strategy != Z_HUFFMAN_ONLY && s->strategy != Z_RLE) { + s->match_length = longest_match (s, hash_head); + } else if (s->strategy == Z_RLE && s->strstart - hash_head == 1) { + s->match_length = longest_match_fast (s, hash_head); + } + /* longest_match() or longest_match_fast() sets match_start */ + + if (s->match_length <= 5 && (s->strategy == Z_FILTERED +#if TOO_FAR <= 32767 + || (s->match_length == MIN_MATCH && + s->strstart - s->match_start > TOO_FAR) +#endif + )) { + + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s->match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s->prev_length >= MIN_MATCH && s->match_length <= s->prev_length) { + uInt max_insert = s->strstart + s->lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + check_match(s, s->strstart-1, s->prev_match, s->prev_length); + + _tr_tally_dist(s, s->strstart -1 - s->prev_match, + s->prev_length - MIN_MATCH, bflush); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s->lookahead -= s->prev_length-1; + s->prev_length -= 2; + do { + if (++s->strstart <= max_insert) { + INSERT_STRING(s, s->strstart, hash_head); + } + } while (--s->prev_length != 0); + s->match_available = 0; + s->match_length = MIN_MATCH-1; + s->strstart++; + + if (bflush) FLUSH_BLOCK(s, 0); + + } else if (s->match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + if (bflush) { + FLUSH_BLOCK_ONLY(s, 0); + } + s->strstart++; + s->lookahead--; + if (s->strm->avail_out == 0) return need_more; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s->match_available = 1; + s->strstart++; + s->lookahead--; + } + } + Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s->match_available) { + Tracevv((stderr,"%c", s->window[s->strstart-1])); + _tr_tally_lit(s, s->window[s->strstart-1], bflush); + s->match_available = 0; + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif /* FASTEST */ + +#if 0 +/* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one. Do not maintain a hash table. (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ +local block_state deflate_rle(s, flush) + deflate_state *s; + int flush; +{ + int bflush; /* set if current block must be flushed */ + uInt run; /* length of run */ + uInt max; /* maximum length of run */ + uInt prev; /* byte at distance one to match */ + Bytef *scan; /* scan for end of run */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the longest encodable run. + */ + if (s->lookahead < MAX_MATCH) { + fill_window(s); + if (s->lookahead < MAX_MATCH && flush == Z_NO_FLUSH) { + return need_more; + } + if (s->lookahead == 0) break; /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + run = 0; + if (s->strstart > 0) { /* if there is a previous byte, that is */ + max = s->lookahead < MAX_MATCH ? s->lookahead : MAX_MATCH; + scan = s->window + s->strstart - 1; + prev = *scan++; + do { + if (*scan++ != prev) + break; + } while (++run < max); + } + + /* Emit match if have run of MIN_MATCH or longer, else emit literal */ + if (run >= MIN_MATCH) { + check_match(s, s->strstart, s->strstart - 1, run); + _tr_tally_dist(s, 1, run - MIN_MATCH, bflush); + s->lookahead -= run; + s->strstart += run; + } else { + /* No match, output a literal byte */ + Tracevv((stderr,"%c", s->window[s->strstart])); + _tr_tally_lit (s, s->window[s->strstart], bflush); + s->lookahead--; + s->strstart++; + } + if (bflush) FLUSH_BLOCK(s, 0); + } + FLUSH_BLOCK(s, flush == Z_FINISH); + return flush == Z_FINISH ? finish_done : block_done; +} +#endif diff --git a/src/lib/zlib/deflate.h b/src/lib/zlib/deflate.h new file mode 100644 index 00000000000..05a5ab3a2c1 --- /dev/null +++ b/src/lib/zlib/deflate.h @@ -0,0 +1,331 @@ +/* deflate.h -- internal compression state + * Copyright (C) 1995-2004 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef DEFLATE_H +#define DEFLATE_H + +#include "zutil.h" + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer creation by deflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip encoding + should be left enabled. */ +#ifndef NO_GZIP +# define GZIP +#endif + +/* =========================================================================== + * Internal compression state. + */ + +#define LENGTH_CODES 29 +/* number of length codes, not counting the special END_BLOCK code */ + +#define LITERALS 256 +/* number of literal bytes 0..255 */ + +#define L_CODES (LITERALS+1+LENGTH_CODES) +/* number of Literal or Length codes, including the END_BLOCK code */ + +#define D_CODES 30 +/* number of distance codes */ + +#define BL_CODES 19 +/* number of codes used to transfer the bit lengths */ + +#define HEAP_SIZE (2*L_CODES+1) +/* maximum heap size */ + +#define MAX_BITS 15 +/* All codes must not exceed MAX_BITS bits */ + +#define INIT_STATE 42 +#define EXTRA_STATE 69 +#define NAME_STATE 73 +#define COMMENT_STATE 91 +#define HCRC_STATE 103 +#define BUSY_STATE 113 +#define FINISH_STATE 666 +/* Stream status */ + + +/* Data structure describing a single value and its code string. */ +typedef struct ct_data_s { + union { + ush freq; /* frequency count */ + ush code; /* bit string */ + } fc; + union { + ush dad; /* father node in Huffman tree */ + ush len; /* length of bit string */ + } dl; +} FAR ct_data; + +#define Freq fc.freq +#define Code fc.code +#define Dad dl.dad +#define Len dl.len + +typedef struct static_tree_desc_s static_tree_desc; + +typedef struct tree_desc_s { + ct_data *dyn_tree; /* the dynamic tree */ + int max_code; /* largest code with non zero frequency */ + static_tree_desc *stat_desc; /* the corresponding static tree */ +} FAR tree_desc; + +typedef ush Pos; +typedef Pos FAR Posf; +typedef unsigned IPos; + +/* A Pos is an index in the character window. We use short instead of int to + * save space in the various tables. IPos is used only for parameter passing. + */ + +typedef struct internal_state { + z_streamp strm; /* pointer back to this zlib stream */ + int status; /* as the name implies */ + Bytef *pending_buf; /* output still pending */ + ulg pending_buf_size; /* size of pending_buf */ + Bytef *pending_out; /* next pending byte to output to the stream */ + uInt pending; /* nb of bytes in the pending buffer */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + gz_headerp gzhead; /* gzip header information to write */ + uInt gzindex; /* where in extra, name, or comment */ + Byte method; /* STORED (for zip only) or DEFLATED */ + int last_flush; /* value of flush param for previous deflate call */ + + /* used by deflate.c: */ + + uInt w_size; /* LZ77 window size (32K by default) */ + uInt w_bits; /* log2(w_size) (8..16) */ + uInt w_mask; /* w_size - 1 */ + + Bytef *window; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. Also, it limits + * the window size to 64K, which is quite useful on MSDOS. + * To do: use the user input buffer as sliding window. + */ + + ulg window_size; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + Posf *prev; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + Posf *head; /* Heads of the hash chains or NIL. */ + + uInt ins_h; /* hash index of string to be inserted */ + uInt hash_size; /* number of elements in hash table */ + uInt hash_bits; /* log2(hash_size) */ + uInt hash_mask; /* hash_size-1 */ + + uInt hash_shift; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + long block_start; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + uInt match_length; /* length of best match */ + IPos prev_match; /* previous match */ + int match_available; /* set if previous match exists */ + uInt strstart; /* start of string to insert */ + uInt match_start; /* start of matching string */ + uInt lookahead; /* number of valid bytes ahead in window */ + + uInt prev_length; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + uInt max_chain_length; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + uInt max_lazy_match; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ +# define max_insert_length max_lazy_match + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + int level; /* compression level (1..9) */ + int strategy; /* favor or force Huffman coding*/ + + uInt good_match; + /* Use a faster search when the previous match is longer than this */ + + int nice_match; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + /* Didn't use ct_data typedef below to supress compiler warning */ + struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + struct tree_desc_s l_desc; /* desc. for literal tree */ + struct tree_desc_s d_desc; /* desc. for distance tree */ + struct tree_desc_s bl_desc; /* desc. for bit length tree */ + + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + int heap_len; /* number of elements in the heap */ + int heap_max; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + uch depth[2*L_CODES+1]; + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + uchf *l_buf; /* buffer for literals or lengths */ + + uInt lit_bufsize; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + uInt last_lit; /* running index in l_buf */ + + ushf *d_buf; + /* Buffer for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + ulg opt_len; /* bit length of current block with optimal trees */ + ulg static_len; /* bit length of current block with static trees */ + uInt matches; /* number of string matches in current block */ + int last_eob_len; /* bit length of EOB code for last block */ + +#ifdef DEBUG + ulg compressed_len; /* total bit length of compressed file mod 2^32 */ + ulg bits_sent; /* bit length of compressed data sent mod 2^32 */ +#endif + + ush bi_buf; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + int bi_valid; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + +} FAR deflate_state; + +/* Output a byte on the stream. + * IN assertion: there is enough room in pending_buf. + */ +#define put_byte(s, c) {s->pending_buf[s->pending++] = (c);} + + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +/* Minimum amount of lookahead, except at the end of the input file. + * See deflate.c for comments about the MIN_MATCH+1. + */ + +#define MAX_DIST(s) ((s)->w_size-MIN_LOOKAHEAD) +/* In order to simplify the code, particularly on 16 bit machines, match + * distances are limited to MAX_DIST instead of WSIZE. + */ + + /* in trees.c */ +void _tr_init OF((deflate_state *s)); +int _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); +void _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); +void _tr_align OF((deflate_state *s)); +void _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, + int eof)); + +#define d_code(dist) \ + ((dist) < 256 ? _dist_code[dist] : _dist_code[256+((dist)>>7)]) +/* Mapping from a distance to a distance code. dist is the distance - 1 and + * must not have side effects. _dist_code[256] and _dist_code[257] are never + * used. + */ + +#ifndef DEBUG +/* Inline versions of _tr_tally for speed: */ + +#if defined(GEN_TREES_H) || !defined(STDC) + extern uch _length_code[]; + extern uch _dist_code[]; +#else + extern const uch _length_code[]; + extern const uch _dist_code[]; +#endif + +# define _tr_tally_lit(s, c, flush) \ + { uch cc = (c); \ + s->d_buf[s->last_lit] = 0; \ + s->l_buf[s->last_lit++] = cc; \ + s->dyn_ltree[cc].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +# define _tr_tally_dist(s, distance, length, flush) \ + { uch len = (length); \ + ush dist = (distance); \ + s->d_buf[s->last_lit] = dist; \ + s->l_buf[s->last_lit++] = len; \ + dist--; \ + s->dyn_ltree[_length_code[len]+LITERALS+1].Freq++; \ + s->dyn_dtree[d_code(dist)].Freq++; \ + flush = (s->last_lit == s->lit_bufsize-1); \ + } +#else +# define _tr_tally_lit(s, c, flush) flush = _tr_tally(s, 0, c) +# define _tr_tally_dist(s, distance, length, flush) \ + flush = _tr_tally(s, distance, length) +#endif + +#endif /* DEFLATE_H */ diff --git a/src/lib/zlib/example.c b/src/lib/zlib/example.c new file mode 100644 index 00000000000..6c8a0ee7633 --- /dev/null +++ b/src/lib/zlib/example.c @@ -0,0 +1,565 @@ +/* example.c -- usage example of the zlib compression library + * Copyright (C) 1995-2004 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include <stdio.h> +#include "zlib.h" + +#ifdef STDC +# include <string.h> +# include <stdlib.h> +#endif + +#if defined(VMS) || defined(RISCOS) +# define TESTFILE "foo-gz" +#else +# define TESTFILE "foo.gz" +#endif + +#define CHECK_ERR(err, msg) { \ + if (err != Z_OK) { \ + fprintf(stderr, "%s error: %d\n", msg, err); \ + exit(1); \ + } \ +} + +const char hello[] = "hello, hello!"; +/* "hello world" would be more standard, but the repeated "hello" + * stresses the compression code better, sorry... + */ + +const char dictionary[] = "hello"; +uLong dictId; /* Adler32 value of the dictionary */ + +void test_compress OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_gzio OF((const char *fname, + Byte *uncompr, uLong uncomprLen)); +void test_deflate OF((Byte *compr, uLong comprLen)); +void test_inflate OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_large_deflate OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_large_inflate OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_flush OF((Byte *compr, uLong *comprLen)); +void test_sync OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +void test_dict_deflate OF((Byte *compr, uLong comprLen)); +void test_dict_inflate OF((Byte *compr, uLong comprLen, + Byte *uncompr, uLong uncomprLen)); +int main OF((int argc, char *argv[])); + +/* =========================================================================== + * Test compress() and uncompress() + */ +void test_compress(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + uLong len = (uLong)strlen(hello)+1; + + err = compress(compr, &comprLen, (const Bytef*)hello, len); + CHECK_ERR(err, "compress"); + + strcpy((char*)uncompr, "garbage"); + + err = uncompress(uncompr, &uncomprLen, compr, comprLen); + CHECK_ERR(err, "uncompress"); + + if (strcmp((char*)uncompr, hello)) { + fprintf(stderr, "bad uncompress\n"); + exit(1); + } else { + printf("uncompress(): %s\n", (char *)uncompr); + } +} + +/* =========================================================================== + * Test read/write of .gz files + */ +void test_gzio(fname, uncompr, uncomprLen) + const char *fname; /* compressed file name */ + Byte *uncompr; + uLong uncomprLen; +{ +#ifdef NO_GZCOMPRESS + fprintf(stderr, "NO_GZCOMPRESS -- gz* functions cannot compress\n"); +#else + int err; + int len = (int)strlen(hello)+1; + gzFile file; + z_off_t pos; + + file = gzopen(fname, "wb"); + if (file == NULL) { + fprintf(stderr, "gzopen error\n"); + exit(1); + } + gzputc(file, 'h'); + if (gzputs(file, "ello") != 4) { + fprintf(stderr, "gzputs err: %s\n", gzerror(file, &err)); + exit(1); + } + if (gzprintf(file, ", %s!", "hello") != 8) { + fprintf(stderr, "gzprintf err: %s\n", gzerror(file, &err)); + exit(1); + } + gzseek(file, 1L, SEEK_CUR); /* add one zero byte */ + gzclose(file); + + file = gzopen(fname, "rb"); + if (file == NULL) { + fprintf(stderr, "gzopen error\n"); + exit(1); + } + strcpy((char*)uncompr, "garbage"); + + if (gzread(file, uncompr, (unsigned)uncomprLen) != len) { + fprintf(stderr, "gzread err: %s\n", gzerror(file, &err)); + exit(1); + } + if (strcmp((char*)uncompr, hello)) { + fprintf(stderr, "bad gzread: %s\n", (char*)uncompr); + exit(1); + } else { + printf("gzread(): %s\n", (char*)uncompr); + } + + pos = gzseek(file, -8L, SEEK_CUR); + if (pos != 6 || gztell(file) != pos) { + fprintf(stderr, "gzseek error, pos=%ld, gztell=%ld\n", + (long)pos, (long)gztell(file)); + exit(1); + } + + if (gzgetc(file) != ' ') { + fprintf(stderr, "gzgetc error\n"); + exit(1); + } + + if (gzungetc(' ', file) != ' ') { + fprintf(stderr, "gzungetc error\n"); + exit(1); + } + + gzgets(file, (char*)uncompr, (int)uncomprLen); + if (strlen((char*)uncompr) != 7) { /* " hello!" */ + fprintf(stderr, "gzgets err after gzseek: %s\n", gzerror(file, &err)); + exit(1); + } + if (strcmp((char*)uncompr, hello + 6)) { + fprintf(stderr, "bad gzgets after gzseek\n"); + exit(1); + } else { + printf("gzgets() after gzseek: %s\n", (char*)uncompr); + } + + gzclose(file); +#endif +} + +/* =========================================================================== + * Test deflate() with small buffers + */ +void test_deflate(compr, comprLen) + Byte *compr; + uLong comprLen; +{ + z_stream c_stream; /* compression stream */ + int err; + uLong len = (uLong)strlen(hello)+1; + + c_stream.zalloc = (alloc_func)0; + c_stream.zfree = (free_func)0; + c_stream.opaque = (voidpf)0; + + err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION); + CHECK_ERR(err, "deflateInit"); + + c_stream.next_in = (Bytef*)hello; + c_stream.next_out = compr; + + while (c_stream.total_in != len && c_stream.total_out < comprLen) { + c_stream.avail_in = c_stream.avail_out = 1; /* force small buffers */ + err = deflate(&c_stream, Z_NO_FLUSH); + CHECK_ERR(err, "deflate"); + } + /* Finish the stream, still forcing small buffers: */ + for (;;) { + c_stream.avail_out = 1; + err = deflate(&c_stream, Z_FINISH); + if (err == Z_STREAM_END) break; + CHECK_ERR(err, "deflate"); + } + + err = deflateEnd(&c_stream); + CHECK_ERR(err, "deflateEnd"); +} + +/* =========================================================================== + * Test inflate() with small buffers + */ +void test_inflate(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + z_stream d_stream; /* decompression stream */ + + strcpy((char*)uncompr, "garbage"); + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = compr; + d_stream.avail_in = 0; + d_stream.next_out = uncompr; + + err = inflateInit(&d_stream); + CHECK_ERR(err, "inflateInit"); + + while (d_stream.total_out < uncomprLen && d_stream.total_in < comprLen) { + d_stream.avail_in = d_stream.avail_out = 1; /* force small buffers */ + err = inflate(&d_stream, Z_NO_FLUSH); + if (err == Z_STREAM_END) break; + CHECK_ERR(err, "inflate"); + } + + err = inflateEnd(&d_stream); + CHECK_ERR(err, "inflateEnd"); + + if (strcmp((char*)uncompr, hello)) { + fprintf(stderr, "bad inflate\n"); + exit(1); + } else { + printf("inflate(): %s\n", (char *)uncompr); + } +} + +/* =========================================================================== + * Test deflate() with large buffers and dynamic change of compression level + */ +void test_large_deflate(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + z_stream c_stream; /* compression stream */ + int err; + + c_stream.zalloc = (alloc_func)0; + c_stream.zfree = (free_func)0; + c_stream.opaque = (voidpf)0; + + err = deflateInit(&c_stream, Z_BEST_SPEED); + CHECK_ERR(err, "deflateInit"); + + c_stream.next_out = compr; + c_stream.avail_out = (uInt)comprLen; + + /* At this point, uncompr is still mostly zeroes, so it should compress + * very well: + */ + c_stream.next_in = uncompr; + c_stream.avail_in = (uInt)uncomprLen; + err = deflate(&c_stream, Z_NO_FLUSH); + CHECK_ERR(err, "deflate"); + if (c_stream.avail_in != 0) { + fprintf(stderr, "deflate not greedy\n"); + exit(1); + } + + /* Feed in already compressed data and switch to no compression: */ + deflateParams(&c_stream, Z_NO_COMPRESSION, Z_DEFAULT_STRATEGY); + c_stream.next_in = compr; + c_stream.avail_in = (uInt)comprLen/2; + err = deflate(&c_stream, Z_NO_FLUSH); + CHECK_ERR(err, "deflate"); + + /* Switch back to compressing mode: */ + deflateParams(&c_stream, Z_BEST_COMPRESSION, Z_FILTERED); + c_stream.next_in = uncompr; + c_stream.avail_in = (uInt)uncomprLen; + err = deflate(&c_stream, Z_NO_FLUSH); + CHECK_ERR(err, "deflate"); + + err = deflate(&c_stream, Z_FINISH); + if (err != Z_STREAM_END) { + fprintf(stderr, "deflate should report Z_STREAM_END\n"); + exit(1); + } + err = deflateEnd(&c_stream); + CHECK_ERR(err, "deflateEnd"); +} + +/* =========================================================================== + * Test inflate() with large buffers + */ +void test_large_inflate(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + z_stream d_stream; /* decompression stream */ + + strcpy((char*)uncompr, "garbage"); + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = compr; + d_stream.avail_in = (uInt)comprLen; + + err = inflateInit(&d_stream); + CHECK_ERR(err, "inflateInit"); + + for (;;) { + d_stream.next_out = uncompr; /* discard the output */ + d_stream.avail_out = (uInt)uncomprLen; + err = inflate(&d_stream, Z_NO_FLUSH); + if (err == Z_STREAM_END) break; + CHECK_ERR(err, "large inflate"); + } + + err = inflateEnd(&d_stream); + CHECK_ERR(err, "inflateEnd"); + + if (d_stream.total_out != 2*uncomprLen + comprLen/2) { + fprintf(stderr, "bad large inflate: %ld\n", d_stream.total_out); + exit(1); + } else { + printf("large_inflate(): OK\n"); + } +} + +/* =========================================================================== + * Test deflate() with full flush + */ +void test_flush(compr, comprLen) + Byte *compr; + uLong *comprLen; +{ + z_stream c_stream; /* compression stream */ + int err; + uInt len = (uInt)strlen(hello)+1; + + c_stream.zalloc = (alloc_func)0; + c_stream.zfree = (free_func)0; + c_stream.opaque = (voidpf)0; + + err = deflateInit(&c_stream, Z_DEFAULT_COMPRESSION); + CHECK_ERR(err, "deflateInit"); + + c_stream.next_in = (Bytef*)hello; + c_stream.next_out = compr; + c_stream.avail_in = 3; + c_stream.avail_out = (uInt)*comprLen; + err = deflate(&c_stream, Z_FULL_FLUSH); + CHECK_ERR(err, "deflate"); + + compr[3]++; /* force an error in first compressed block */ + c_stream.avail_in = len - 3; + + err = deflate(&c_stream, Z_FINISH); + if (err != Z_STREAM_END) { + CHECK_ERR(err, "deflate"); + } + err = deflateEnd(&c_stream); + CHECK_ERR(err, "deflateEnd"); + + *comprLen = c_stream.total_out; +} + +/* =========================================================================== + * Test inflateSync() + */ +void test_sync(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + z_stream d_stream; /* decompression stream */ + + strcpy((char*)uncompr, "garbage"); + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = compr; + d_stream.avail_in = 2; /* just read the zlib header */ + + err = inflateInit(&d_stream); + CHECK_ERR(err, "inflateInit"); + + d_stream.next_out = uncompr; + d_stream.avail_out = (uInt)uncomprLen; + + inflate(&d_stream, Z_NO_FLUSH); + CHECK_ERR(err, "inflate"); + + d_stream.avail_in = (uInt)comprLen-2; /* read all compressed data */ + err = inflateSync(&d_stream); /* but skip the damaged part */ + CHECK_ERR(err, "inflateSync"); + + err = inflate(&d_stream, Z_FINISH); + if (err != Z_DATA_ERROR) { + fprintf(stderr, "inflate should report DATA_ERROR\n"); + /* Because of incorrect adler32 */ + exit(1); + } + err = inflateEnd(&d_stream); + CHECK_ERR(err, "inflateEnd"); + + printf("after inflateSync(): hel%s\n", (char *)uncompr); +} + +/* =========================================================================== + * Test deflate() with preset dictionary + */ +void test_dict_deflate(compr, comprLen) + Byte *compr; + uLong comprLen; +{ + z_stream c_stream; /* compression stream */ + int err; + + c_stream.zalloc = (alloc_func)0; + c_stream.zfree = (free_func)0; + c_stream.opaque = (voidpf)0; + + err = deflateInit(&c_stream, Z_BEST_COMPRESSION); + CHECK_ERR(err, "deflateInit"); + + err = deflateSetDictionary(&c_stream, + (const Bytef*)dictionary, sizeof(dictionary)); + CHECK_ERR(err, "deflateSetDictionary"); + + dictId = c_stream.adler; + c_stream.next_out = compr; + c_stream.avail_out = (uInt)comprLen; + + c_stream.next_in = (Bytef*)hello; + c_stream.avail_in = (uInt)strlen(hello)+1; + + err = deflate(&c_stream, Z_FINISH); + if (err != Z_STREAM_END) { + fprintf(stderr, "deflate should report Z_STREAM_END\n"); + exit(1); + } + err = deflateEnd(&c_stream); + CHECK_ERR(err, "deflateEnd"); +} + +/* =========================================================================== + * Test inflate() with a preset dictionary + */ +void test_dict_inflate(compr, comprLen, uncompr, uncomprLen) + Byte *compr, *uncompr; + uLong comprLen, uncomprLen; +{ + int err; + z_stream d_stream; /* decompression stream */ + + strcpy((char*)uncompr, "garbage"); + + d_stream.zalloc = (alloc_func)0; + d_stream.zfree = (free_func)0; + d_stream.opaque = (voidpf)0; + + d_stream.next_in = compr; + d_stream.avail_in = (uInt)comprLen; + + err = inflateInit(&d_stream); + CHECK_ERR(err, "inflateInit"); + + d_stream.next_out = uncompr; + d_stream.avail_out = (uInt)uncomprLen; + + for (;;) { + err = inflate(&d_stream, Z_NO_FLUSH); + if (err == Z_STREAM_END) break; + if (err == Z_NEED_DICT) { + if (d_stream.adler != dictId) { + fprintf(stderr, "unexpected dictionary"); + exit(1); + } + err = inflateSetDictionary(&d_stream, (const Bytef*)dictionary, + sizeof(dictionary)); + } + CHECK_ERR(err, "inflate with dict"); + } + + err = inflateEnd(&d_stream); + CHECK_ERR(err, "inflateEnd"); + + if (strcmp((char*)uncompr, hello)) { + fprintf(stderr, "bad inflate with dict\n"); + exit(1); + } else { + printf("inflate with dictionary: %s\n", (char *)uncompr); + } +} + +/* =========================================================================== + * Usage: example [output.gz [input.gz]] + */ + +int main(argc, argv) + int argc; + char *argv[]; +{ + Byte *compr, *uncompr; + uLong comprLen = 10000*sizeof(int); /* don't overflow on MSDOS */ + uLong uncomprLen = comprLen; + static const char* myVersion = ZLIB_VERSION; + + if (zlibVersion()[0] != myVersion[0]) { + fprintf(stderr, "incompatible zlib version\n"); + exit(1); + + } else if (strcmp(zlibVersion(), ZLIB_VERSION) != 0) { + fprintf(stderr, "warning: different zlib version\n"); + } + + printf("zlib version %s = 0x%04x, compile flags = 0x%lx\n", + ZLIB_VERSION, ZLIB_VERNUM, zlibCompileFlags()); + + compr = (Byte*)calloc((uInt)comprLen, 1); + uncompr = (Byte*)calloc((uInt)uncomprLen, 1); + /* compr and uncompr are cleared to avoid reading uninitialized + * data and to ensure that uncompr compresses well. + */ + if (compr == Z_NULL || uncompr == Z_NULL) { + printf("out of memory\n"); + exit(1); + } + test_compress(compr, comprLen, uncompr, uncomprLen); + + test_gzio((argc > 1 ? argv[1] : TESTFILE), + uncompr, uncomprLen); + + test_deflate(compr, comprLen); + test_inflate(compr, comprLen, uncompr, uncomprLen); + + test_large_deflate(compr, comprLen, uncompr, uncomprLen); + test_large_inflate(compr, comprLen, uncompr, uncomprLen); + + test_flush(compr, &comprLen); + test_sync(compr, comprLen, uncompr, uncomprLen); + comprLen = uncomprLen; + + test_dict_deflate(compr, comprLen); + test_dict_inflate(compr, comprLen, uncompr, uncomprLen); + + free(compr); + free(uncompr); + + return 0; +} diff --git a/src/lib/zlib/gzio.c b/src/lib/zlib/gzio.c new file mode 100644 index 00000000000..7e90f4928fc --- /dev/null +++ b/src/lib/zlib/gzio.c @@ -0,0 +1,1026 @@ +/* gzio.c -- IO on .gz files + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + * + * Compile this file with -DNO_GZCOMPRESS to avoid the compression code. + */ + +/* @(#) $Id$ */ + +#include <stdio.h> + +#include "zutil.h" + +#ifdef NO_DEFLATE /* for compatibility with old definition */ +# define NO_GZCOMPRESS +#endif + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +#ifndef Z_BUFSIZE +# ifdef MAXSEG_64K +# define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */ +# else +# define Z_BUFSIZE 16384 +# endif +#endif +#ifndef Z_PRINTF_BUFSIZE +# define Z_PRINTF_BUFSIZE 4096 +#endif + +#ifdef __MVS__ +# pragma map (fdopen , "\174\174FDOPEN") + FILE *fdopen(int, const char *); +#endif + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern void free OF((voidpf ptr)); +#endif + +#define ALLOC(size) malloc(size) +#define TRYFREE(p) {if (p) free(p);} + +static int const gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define RESERVED 0xE0 /* bits 5..7: reserved */ + +typedef struct gz_stream { + z_stream stream; + int z_err; /* error code for last stream operation */ + int z_eof; /* set if end of input file */ + FILE *file; /* .gz file */ + Byte *inbuf; /* input buffer */ + Byte *outbuf; /* output buffer */ + uLong crc; /* crc32 of uncompressed data */ + char *msg; /* error message */ + char *path; /* path name for debugging only */ + int transparent; /* 1 if input file is not a .gz file */ + char mode; /* 'w' or 'r' */ + z_off_t start; /* start of compressed data in file (header skipped) */ + z_off_t in; /* bytes into deflate or inflate */ + z_off_t out; /* bytes out of deflate or inflate */ + int back; /* one character push-back */ + int last; /* true if push-back is last character */ +} gz_stream; + + +local gzFile gz_open OF((const char *path, const char *mode, int fd)); +local int do_flush OF((gzFile file, int flush)); +local int get_byte OF((gz_stream *s)); +local void check_header OF((gz_stream *s)); +local int destroy OF((gz_stream *s)); +local void putLong OF((FILE *file, uLong x)); +local uLong getLong OF((gz_stream *s)); + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb"). The file is given either by file descriptor + or path name (if fd == -1). + gz_open returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). +*/ +local gzFile gz_open (path, mode, fd) + const char *path; + const char *mode; + int fd; +{ + int err; + int level = Z_DEFAULT_COMPRESSION; /* compression level */ + int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */ + char *p = (char*)mode; + gz_stream *s; + char fmode[80]; /* copy of mode, without the compression level */ + char *m = fmode; + + if (!path || !mode) return Z_NULL; + + s = (gz_stream *)ALLOC(sizeof(gz_stream)); + if (!s) return Z_NULL; + + s->stream.zalloc = (alloc_func)0; + s->stream.zfree = (free_func)0; + s->stream.opaque = (voidpf)0; + s->stream.next_in = s->inbuf = Z_NULL; + s->stream.next_out = s->outbuf = Z_NULL; + s->stream.avail_in = s->stream.avail_out = 0; + s->file = NULL; + s->z_err = Z_OK; + s->z_eof = 0; + s->in = 0; + s->out = 0; + s->back = EOF; + s->crc = crc32(0L, Z_NULL, 0); + s->msg = NULL; + s->transparent = 0; + + s->path = (char*)ALLOC(strlen(path)+1); + if (s->path == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + strcpy(s->path, path); /* do this early for debugging */ + + s->mode = '\0'; + do { + if (*p == 'r') s->mode = 'r'; + if (*p == 'w' || *p == 'a') s->mode = 'w'; + if (*p >= '0' && *p <= '9') { + level = *p - '0'; + } else if (*p == 'f') { + strategy = Z_FILTERED; + } else if (*p == 'h') { + strategy = Z_HUFFMAN_ONLY; + } else if (*p == 'R') { + strategy = Z_RLE; + } else { + *m++ = *p; /* copy the mode */ + } + } while (*p++ && m != fmode + sizeof(fmode)); + if (s->mode == '\0') return destroy(s), (gzFile)Z_NULL; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateInit2(&(s->stream), level, + Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy); + /* windowBits is passed < 0 to suppress zlib header */ + + s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); +#endif + if (err != Z_OK || s->outbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } else { + s->stream.next_in = s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); + + err = inflateInit2(&(s->stream), -MAX_WBITS); + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are + * present after the compressed stream. + */ + if (err != Z_OK || s->inbuf == Z_NULL) { + return destroy(s), (gzFile)Z_NULL; + } + } + s->stream.avail_out = Z_BUFSIZE; + + errno = 0; + s->file = fd < 0 ? F_OPEN(path, fmode) : (FILE*)fdopen(fd, fmode); + + if (s->file == NULL) { + return destroy(s), (gzFile)Z_NULL; + } + if (s->mode == 'w') { + /* Write a very simple .gz header: + */ + fprintf(s->file, "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], gz_magic[1], + Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 0 /*xflags*/, OS_CODE); + s->start = 10L; + /* We use 10L instead of ftell(s->file) to because ftell causes an + * fflush on some systems. This version of the library doesn't use + * start anyway in write mode, so this initialization is not + * necessary. + */ + } else { + check_header(s); /* skip the .gz header */ + s->start = ftell(s->file) - s->stream.avail_in; + } + + return (gzFile)s; +} + +/* =========================================================================== + Opens a gzip (.gz) file for reading or writing. +*/ +gzFile ZEXPORT gzopen (path, mode) + const char *path; + const char *mode; +{ + return gz_open (path, mode, -1); +} + +/* =========================================================================== + Associate a gzFile with the file descriptor fd. fd is not dup'ed here + to mimic the behavio(u)r of fdopen. +*/ +gzFile ZEXPORT gzdopen (fd, mode) + int fd; + const char *mode; +{ + char name[46]; /* allow for up to 128-bit integers */ + + if (fd < 0) return (gzFile)Z_NULL; + sprintf(name, "<fd:%d>", fd); /* for debugging */ + + return gz_open (name, mode, fd); +} + +/* =========================================================================== + * Update the compression level and strategy + */ +int ZEXPORT gzsetparams (file, level, strategy) + gzFile file; + int level; + int strategy; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + /* Make room to allow flushing */ + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + } + s->stream.avail_out = Z_BUFSIZE; + } + + return deflateParams (&(s->stream), level, strategy); +} + +/* =========================================================================== + Read a byte from a gz_stream; update next_in and avail_in. Return EOF + for end of file. + IN assertion: the stream s has been sucessfully opened for reading. +*/ +local int get_byte(s) + gz_stream *s; +{ + if (s->z_eof) return EOF; + if (s->stream.avail_in == 0) { + errno = 0; + s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) s->z_err = Z_ERRNO; + return EOF; + } + s->stream.next_in = s->inbuf; + } + s->stream.avail_in--; + return *(s->stream.next_in)++; +} + +/* =========================================================================== + Check the gzip header of a gz_stream opened for reading. Set the stream + mode to transparent if the gzip magic header is not present; set s->err + to Z_DATA_ERROR if the magic header is present but the rest of the header + is incorrect. + IN assertion: the stream s has already been created sucessfully; + s->stream.avail_in is zero for the first time, but may be non-zero + for concatenated .gz files. +*/ +local void check_header(s) + gz_stream *s; +{ + int method; /* method byte */ + int flags; /* flags byte */ + uInt len; + int c; + + /* Assure two bytes in the buffer so we can peek ahead -- handle case + where first byte of header is at the end of the buffer after the last + gzip segment */ + len = s->stream.avail_in; + if (len < 2) { + if (len) s->inbuf[0] = s->stream.next_in[0]; + errno = 0; + len = (uInt)fread(s->inbuf + len, 1, Z_BUFSIZE >> len, s->file); + if (len == 0 && ferror(s->file)) s->z_err = Z_ERRNO; + s->stream.avail_in += len; + s->stream.next_in = s->inbuf; + if (s->stream.avail_in < 2) { + s->transparent = s->stream.avail_in; + return; + } + } + + /* Peek ahead to check the gzip magic header */ + if (s->stream.next_in[0] != gz_magic[0] || + s->stream.next_in[1] != gz_magic[1]) { + s->transparent = 1; + return; + } + s->stream.avail_in -= 2; + s->stream.next_in += 2; + + /* Check the rest of the gzip header */ + method = get_byte(s); + flags = get_byte(s); + if (method != Z_DEFLATED || (flags & RESERVED) != 0) { + s->z_err = Z_DATA_ERROR; + return; + } + + /* Discard time, xflags and OS code: */ + for (len = 0; len < 6; len++) (void)get_byte(s); + + if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */ + len = (uInt)get_byte(s); + len += ((uInt)get_byte(s))<<8; + /* len is garbage if EOF but the loop below will quit anyway */ + while (len-- != 0 && get_byte(s) != EOF) ; + } + if ((flags & ORIG_NAME) != 0) { /* skip the original file name */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & COMMENT) != 0) { /* skip the .gz file comment */ + while ((c = get_byte(s)) != 0 && c != EOF) ; + } + if ((flags & HEAD_CRC) != 0) { /* skip the header crc */ + for (len = 0; len < 2; len++) (void)get_byte(s); + } + s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK; +} + + /* =========================================================================== + * Cleanup then free the given gz_stream. Return a zlib error code. + Try freeing in the reverse order of allocations. + */ +local int destroy (s) + gz_stream *s; +{ + int err = Z_OK; + + if (!s) return Z_STREAM_ERROR; + + TRYFREE(s->msg); + + if (s->stream.state != NULL) { + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + err = Z_STREAM_ERROR; +#else + err = deflateEnd(&(s->stream)); +#endif + } else if (s->mode == 'r') { + err = inflateEnd(&(s->stream)); + } + } + if (s->file != NULL && fclose(s->file)) { +#ifdef ESPIPE + if (errno != ESPIPE) /* fclose is broken for pipes in HP/UX */ +#endif + err = Z_ERRNO; + } + if (s->z_err < 0) err = s->z_err; + + TRYFREE(s->inbuf); + TRYFREE(s->outbuf); + TRYFREE(s->path); + TRYFREE(s); + return err; +} + +/* =========================================================================== + Reads the given number of uncompressed bytes from the compressed file. + gzread returns the number of bytes actually read (0 for end of file). +*/ +int ZEXPORT gzread (file, buf, len) + gzFile file; + voidp buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + Bytef *start = (Bytef*)buf; /* starting point for crc computation */ + Byte *next_out; /* == stream.next_out but not forced far (for MSDOS) */ + + if (s == NULL || s->mode != 'r') return Z_STREAM_ERROR; + + if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1; + if (s->z_err == Z_STREAM_END) return 0; /* EOF */ + + next_out = (Byte*)buf; + s->stream.next_out = (Bytef*)buf; + s->stream.avail_out = len; + + if (s->stream.avail_out && s->back != EOF) { + *next_out++ = s->back; + s->stream.next_out++; + s->stream.avail_out--; + s->back = EOF; + s->out++; + start++; + if (s->last) { + s->z_err = Z_STREAM_END; + return 1; + } + } + + while (s->stream.avail_out != 0) { + + if (s->transparent) { + /* Copy first the lookahead bytes: */ + uInt n = s->stream.avail_in; + if (n > s->stream.avail_out) n = s->stream.avail_out; + if (n > 0) { + zmemcpy(s->stream.next_out, s->stream.next_in, n); + next_out += n; + s->stream.next_out = next_out; + s->stream.next_in += n; + s->stream.avail_out -= n; + s->stream.avail_in -= n; + } + if (s->stream.avail_out > 0) { + s->stream.avail_out -= + (uInt)fread(next_out, 1, s->stream.avail_out, s->file); + } + len -= s->stream.avail_out; + s->in += len; + s->out += len; + if (len == 0) s->z_eof = 1; + return (int)len; + } + if (s->stream.avail_in == 0 && !s->z_eof) { + + errno = 0; + s->stream.avail_in = (uInt)fread(s->inbuf, 1, Z_BUFSIZE, s->file); + if (s->stream.avail_in == 0) { + s->z_eof = 1; + if (ferror(s->file)) { + s->z_err = Z_ERRNO; + break; + } + } + s->stream.next_in = s->inbuf; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = inflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + + if (s->z_err == Z_STREAM_END) { + /* Check CRC and original size */ + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + start = s->stream.next_out; + + if (getLong(s) != s->crc) { + s->z_err = Z_DATA_ERROR; + } else { + (void)getLong(s); + /* The uncompressed length returned by above getlong() may be + * different from s->out in case of concatenated .gz files. + * Check for such files: + */ + check_header(s); + if (s->z_err == Z_OK) { + inflateReset(&(s->stream)); + s->crc = crc32(0L, Z_NULL, 0); + } + } + } + if (s->z_err != Z_OK || s->z_eof) break; + } + s->crc = crc32(s->crc, start, (uInt)(s->stream.next_out - start)); + + if (len == s->stream.avail_out && + (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO)) + return -1; + return (int)(len - s->stream.avail_out); +} + + +/* =========================================================================== + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ +int ZEXPORT gzgetc(file) + gzFile file; +{ + unsigned char c; + + return gzread(file, &c, 1) == 1 ? c : -1; +} + + +/* =========================================================================== + Push one byte back onto the stream. +*/ +int ZEXPORT gzungetc(c, file) + int c; + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r' || c == EOF || s->back != EOF) return EOF; + s->back = c; + s->out--; + s->last = (s->z_err == Z_STREAM_END); + if (s->last) s->z_err = Z_OK; + s->z_eof = 0; + return c; +} + + +/* =========================================================================== + Reads bytes from the compressed file until len-1 characters are + read, or a newline character is read and transferred to buf, or an + end-of-file condition is encountered. The string is then terminated + with a null character. + gzgets returns buf, or Z_NULL in case of error. + + The current implementation is not optimized at all. +*/ +char * ZEXPORT gzgets(file, buf, len) + gzFile file; + char *buf; + int len; +{ + char *b = buf; + if (buf == Z_NULL || len <= 0) return Z_NULL; + + while (--len > 0 && gzread(file, buf, 1) == 1 && *buf++ != '\n') ; + *buf = '\0'; + return b == buf && len > 0 ? Z_NULL : b; +} + + +#ifndef NO_GZCOMPRESS +/* =========================================================================== + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of bytes actually written (0 in case of error). +*/ +int ZEXPORT gzwrite (file, buf, len) + gzFile file; + voidpc buf; + unsigned len; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.next_in = (Bytef*)buf; + s->stream.avail_in = len; + + while (s->stream.avail_in != 0) { + + if (s->stream.avail_out == 0) { + + s->stream.next_out = s->outbuf; + if (fwrite(s->outbuf, 1, Z_BUFSIZE, s->file) != Z_BUFSIZE) { + s->z_err = Z_ERRNO; + break; + } + s->stream.avail_out = Z_BUFSIZE; + } + s->in += s->stream.avail_in; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), Z_NO_FLUSH); + s->in -= s->stream.avail_in; + s->out -= s->stream.avail_out; + if (s->z_err != Z_OK) break; + } + s->crc = crc32(s->crc, (const Bytef *)buf, len); + + return (int)(len - s->stream.avail_in); +} + + +/* =========================================================================== + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). +*/ +#ifdef STDC +#include <stdarg.h> + +int ZEXPORTVA gzprintf (gzFile file, const char *format, /* args */ ...) +{ + char buf[Z_PRINTF_BUFSIZE]; + va_list va; + int len; + + buf[sizeof(buf) - 1] = 0; + va_start(va, format); +#ifdef NO_vsnprintf +# ifdef HAS_vsprintf_void + (void)vsprintf(buf, format, va); + va_end(va); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = vsprintf(buf, format, va); + va_end(va); +# endif +#else +# ifdef HAS_vsnprintf_void + (void)vsnprintf(buf, sizeof(buf), format, va); + va_end(va); + len = strlen(buf); +# else + len = vsnprintf(buf, sizeof(buf), format, va); + va_end(va); +# endif +#endif + if (len <= 0 || len >= (int)sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, (unsigned)len); +} +#else /* not ANSI C */ + +int ZEXPORTVA gzprintf (file, format, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) + gzFile file; + const char *format; + int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, + a11, a12, a13, a14, a15, a16, a17, a18, a19, a20; +{ + char buf[Z_PRINTF_BUFSIZE]; + int len; + + buf[sizeof(buf) - 1] = 0; +#ifdef NO_snprintf +# ifdef HAS_sprintf_void + sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + for (len = 0; len < sizeof(buf); len++) + if (buf[len] == 0) break; +# else + len = sprintf(buf, format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#else +# ifdef HAS_snprintf_void + snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); + len = strlen(buf); +# else + len = snprintf(buf, sizeof(buf), format, a1, a2, a3, a4, a5, a6, a7, a8, + a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20); +# endif +#endif + if (len <= 0 || len >= sizeof(buf) || buf[sizeof(buf) - 1] != 0) + return 0; + return gzwrite(file, buf, len); +} +#endif + +/* =========================================================================== + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ +int ZEXPORT gzputc(file, c) + gzFile file; + int c; +{ + unsigned char cc = (unsigned char) c; /* required for big endian systems */ + + return gzwrite(file, &cc, 1) == 1 ? (int)cc : -1; +} + + +/* =========================================================================== + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ +int ZEXPORT gzputs(file, s) + gzFile file; + const char *s; +{ + return gzwrite(file, (char*)s, (unsigned)strlen(s)); +} + + +/* =========================================================================== + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. +*/ +local int do_flush (file, flush) + gzFile file; + int flush; +{ + uInt len; + int done = 0; + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; + + s->stream.avail_in = 0; /* should be zero already anyway */ + + for (;;) { + len = Z_BUFSIZE - s->stream.avail_out; + + if (len != 0) { + if ((uInt)fwrite(s->outbuf, 1, len, s->file) != len) { + s->z_err = Z_ERRNO; + return Z_ERRNO; + } + s->stream.next_out = s->outbuf; + s->stream.avail_out = Z_BUFSIZE; + } + if (done) break; + s->out += s->stream.avail_out; + s->z_err = deflate(&(s->stream), flush); + s->out -= s->stream.avail_out; + + /* Ignore the second of two consecutive flushes: */ + if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK; + + /* deflate has finished flushing only when it hasn't used up + * all the available space in the output buffer: + */ + done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END); + + if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break; + } + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} + +int ZEXPORT gzflush (file, flush) + gzFile file; + int flush; +{ + gz_stream *s = (gz_stream*)file; + int err = do_flush (file, flush); + + if (err) return err; + fflush(s->file); + return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; +} +#endif /* NO_GZCOMPRESS */ + +/* =========================================================================== + Sets the starting position for the next gzread or gzwrite on the given + compressed file. The offset represents a number of bytes in the + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error. + SEEK_END is not implemented, returns error. + In this version of the library, gzseek can be extremely slow. +*/ +z_off_t ZEXPORT gzseek (file, offset, whence) + gzFile file; + z_off_t offset; + int whence; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || whence == SEEK_END || + s->z_err == Z_ERRNO || s->z_err == Z_DATA_ERROR) { + return -1L; + } + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return -1L; +#else + if (whence == SEEK_SET) { + offset -= s->in; + } + if (offset < 0) return -1L; + + /* At this point, offset is the number of zero bytes to write. */ + if (s->inbuf == Z_NULL) { + s->inbuf = (Byte*)ALLOC(Z_BUFSIZE); /* for seeking */ + if (s->inbuf == Z_NULL) return -1L; + zmemzero(s->inbuf, Z_BUFSIZE); + } + while (offset > 0) { + uInt size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (uInt)offset; + + size = gzwrite(file, s->inbuf, size); + if (size == 0) return -1L; + + offset -= size; + } + return s->in; +#endif + } + /* Rest of function is for reading only */ + + /* compute absolute position */ + if (whence == SEEK_CUR) { + offset += s->out; + } + if (offset < 0) return -1L; + + if (s->transparent) { + /* map to fseek */ + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + if (fseek(s->file, offset, SEEK_SET) < 0) return -1L; + + s->in = s->out = offset; + return offset; + } + + /* For a negative seek, rewind and use positive seek */ + if (offset >= s->out) { + offset -= s->out; + } else if (gzrewind(file) < 0) { + return -1L; + } + /* offset is now the number of bytes to skip. */ + + if (offset != 0 && s->outbuf == Z_NULL) { + s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); + if (s->outbuf == Z_NULL) return -1L; + } + if (offset && s->back != EOF) { + s->back = EOF; + s->out++; + offset--; + if (s->last) s->z_err = Z_STREAM_END; + } + while (offset > 0) { + int size = Z_BUFSIZE; + if (offset < Z_BUFSIZE) size = (int)offset; + + size = gzread(file, s->outbuf, (uInt)size); + if (size <= 0) return -1L; + offset -= size; + } + return s->out; +} + +/* =========================================================================== + Rewinds input file. +*/ +int ZEXPORT gzrewind (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return -1; + + s->z_err = Z_OK; + s->z_eof = 0; + s->back = EOF; + s->stream.avail_in = 0; + s->stream.next_in = s->inbuf; + s->crc = crc32(0L, Z_NULL, 0); + if (!s->transparent) (void)inflateReset(&s->stream); + s->in = 0; + s->out = 0; + return fseek(s->file, s->start, SEEK_SET); +} + +/* =========================================================================== + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. +*/ +z_off_t ZEXPORT gztell (file) + gzFile file; +{ + return gzseek(file, 0L, SEEK_CUR); +} + +/* =========================================================================== + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ +int ZEXPORT gzeof (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + /* With concatenated compressed files that can have embedded + * crc trailers, z_eof is no longer the only/best indicator of EOF + * on a gz_stream. Handle end-of-stream error explicitly here. + */ + if (s == NULL || s->mode != 'r') return 0; + if (s->z_eof) return 1; + return s->z_err == Z_STREAM_END; +} + +/* =========================================================================== + Returns 1 if reading and doing so transparently, otherwise zero. +*/ +int ZEXPORT gzdirect (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL || s->mode != 'r') return 0; + return s->transparent; +} + +/* =========================================================================== + Outputs a long in LSB order to the given file +*/ +local void putLong (file, x) + FILE *file; + uLong x; +{ + int n; + for (n = 0; n < 4; n++) { + fputc((int)(x & 0xff), file); + x >>= 8; + } +} + +/* =========================================================================== + Reads a long in LSB order from the given gz_stream. Sets z_err in case + of error. +*/ +local uLong getLong (s) + gz_stream *s; +{ + uLong x = (uLong)get_byte(s); + int c; + + x += ((uLong)get_byte(s))<<8; + x += ((uLong)get_byte(s))<<16; + c = get_byte(s); + if (c == EOF) s->z_err = Z_DATA_ERROR; + x += ((uLong)c)<<24; + return x; +} + +/* =========================================================================== + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. +*/ +int ZEXPORT gzclose (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return Z_STREAM_ERROR; + + if (s->mode == 'w') { +#ifdef NO_GZCOMPRESS + return Z_STREAM_ERROR; +#else + if (do_flush (file, Z_FINISH) != Z_OK) + return destroy((gz_stream*)file); + + putLong (s->file, s->crc); + putLong (s->file, (uLong)(s->in & 0xffffffff)); +#endif + } + return destroy((gz_stream*)file); +} + +#ifdef STDC +# define zstrerror(errnum) strerror(errnum) +#else +# define zstrerror(errnum) "" +#endif + +/* =========================================================================== + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ +const char * ZEXPORT gzerror (file, errnum) + gzFile file; + int *errnum; +{ + char *m; + gz_stream *s = (gz_stream*)file; + + if (s == NULL) { + *errnum = Z_STREAM_ERROR; + return (const char*)ERR_MSG(Z_STREAM_ERROR); + } + *errnum = s->z_err; + if (*errnum == Z_OK) return (const char*)""; + + m = (char*)(*errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg); + + if (m == NULL || *m == '\0') m = (char*)ERR_MSG(s->z_err); + + TRYFREE(s->msg); + s->msg = (char*)ALLOC(strlen(s->path) + strlen(m) + 3); + if (s->msg == Z_NULL) return (const char*)ERR_MSG(Z_MEM_ERROR); + strcpy(s->msg, s->path); + strcat(s->msg, ": "); + strcat(s->msg, m); + return (const char*)s->msg; +} + +/* =========================================================================== + Clear the error and end-of-file flags, and do the same for the real file. +*/ +void ZEXPORT gzclearerr (file) + gzFile file; +{ + gz_stream *s = (gz_stream*)file; + + if (s == NULL) return; + if (s->z_err != Z_STREAM_END) s->z_err = Z_OK; + s->z_eof = 0; + clearerr(s->file); +} diff --git a/src/lib/zlib/infback.c b/src/lib/zlib/infback.c new file mode 100644 index 00000000000..455dbc9ee84 --- /dev/null +++ b/src/lib/zlib/infback.c @@ -0,0 +1,623 @@ +/* infback.c -- inflate using a call-back interface + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + This code is largely copied from inflate.c. Normally either infback.o or + inflate.o would be linked into an application--not both. The interface + with inffast.c is retained so that optimized assembler-coded versions of + inflate_fast() can be used with either inflate.c or infback.c. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); + +/* + strm provides memory allocation functions in zalloc and zfree, or + Z_NULL to use the library memory allocation functions. + + windowBits is in the range 8..15, and window is a user-supplied + window and output buffer that is 2**windowBits bytes. + */ +int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size) +z_streamp strm; +int windowBits; +unsigned char FAR *window; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL || window == Z_NULL || + windowBits < 8 || windowBits > 15) + return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *)ZALLOC(strm, 1, + sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->dmax = 32768U; + state->wbits = windowBits; + state->wsize = 1U << windowBits; + state->window = window; + state->write = 0; + state->whave = 0; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +/* Macros for inflateBack(): */ + +/* Load returned state from inflate_fast() */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Set state from registers for inflate_fast() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Assure that some input is available. If input is requested, but denied, + then return a Z_BUF_ERROR from inflateBack(). */ +#define PULL() \ + do { \ + if (have == 0) { \ + have = in(in_desc, &next); \ + if (have == 0) { \ + next = Z_NULL; \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflateBack() + with an error if there is no input available. */ +#define PULLBYTE() \ + do { \ + PULL(); \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflateBack() with + an error. */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Assure that some output space is available, by writing out the window + if it's full. If the write fails, return from inflateBack() with a + Z_BUF_ERROR. */ +#define ROOM() \ + do { \ + if (left == 0) { \ + put = state->window; \ + left = state->wsize; \ + state->whave = left; \ + if (out(out_desc, put, left)) { \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* + strm provides the memory allocation functions and window buffer on input, + and provides information on the unused input on return. For Z_DATA_ERROR + returns, strm will also provide an error message. + + in() and out() are the call-back input and output functions. When + inflateBack() needs more input, it calls in(). When inflateBack() has + filled the window with output, or when it completes with data in the + window, it calls out() to write out the data. The application must not + change the provided input until in() is called again or inflateBack() + returns. The application must not change the window/output buffer until + inflateBack() returns. + + in() and out() are called with a descriptor parameter provided in the + inflateBack() call. This parameter can be a structure that provides the + information required to do the read or write, as well as accumulated + information on the input and output such as totals and check values. + + in() should return zero on failure. out() should return non-zero on + failure. If either in() or out() fails, than inflateBack() returns a + Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it + was in() or out() that caused in the error. Otherwise, inflateBack() + returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format + error, or Z_MEM_ERROR if it could not allocate memory for the state. + inflateBack() can also return Z_STREAM_ERROR if the input parameters + are not correct, i.e. strm is Z_NULL or the state was not initialized. + */ +int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc) +z_streamp strm; +in_func in; +void FAR *in_desc; +out_func out; +void FAR *out_desc; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + /* Check that the strm exists and that the state was initialized */ + if (strm == Z_NULL || strm->state == Z_NULL) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* Reset the state */ + strm->msg = Z_NULL; + state->mode = TYPE; + state->last = 0; + state->whave = 0; + next = strm->next_in; + have = next != Z_NULL ? strm->avail_in : 0; + hold = 0; + bits = 0; + put = state->window; + left = state->wsize; + + /* Inflate until end of block marked as last */ + for (;;) + switch (state->mode) { + case TYPE: + /* determine and dispatch block type */ + if (state->last) { + BYTEBITS(); + state->mode = DONE; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + + case STORED: + /* get and verify stored block length */ + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + + /* copy stored block from input to output */ + while (state->length != 0) { + copy = state->length; + PULL(); + ROOM(); + if (copy > have) copy = have; + if (copy > left) copy = left; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + + case TABLE: + /* get dynamic table entries descriptor */ + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + + /* get code length code lengths (not a typo) */ + state->have = 0; + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + + /* get length and distance code code lengths */ + state->have = 0; + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = (unsigned)(state->lens[state->have - 1]); + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + + case LEN: + /* use inflate_fast() if we have enough input and output */ + if (have >= 6 && left >= 258) { + RESTORE(); + if (state->whave < state->wsize) + state->whave = state->wsize - left; + inflate_fast(strm, state->wsize); + LOAD(); + break; + } + + /* get a literal, length, or end-of-block code */ + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + + /* process literal */ + if (this.op == 0) { + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + ROOM(); + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + } + + /* process end of block */ + if (this.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + + /* invalid code */ + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + + /* length code -- get extra bits, if any */ + state->extra = (unsigned)(this.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + + /* get distance code */ + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + + /* get distance extra bits, if any */ + state->extra = (unsigned)(this.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } + if (state->offset > state->wsize - (state->whave < state->wsize ? + left : 0)) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + + /* copy match from window to output */ + do { + ROOM(); + copy = state->wsize - state->offset; + if (copy < left) { + from = put + copy; + copy = left - copy; + } + else { + from = put - state->offset; + copy = left; + } + if (copy > state->length) copy = state->length; + state->length -= copy; + left -= copy; + do { + *put++ = *from++; + } while (--copy); + } while (state->length != 0); + break; + + case DONE: + /* inflate stream terminated properly -- write leftover output */ + ret = Z_STREAM_END; + if (left < state->wsize) { + if (out(out_desc, state->window, state->wsize - left)) + ret = Z_BUF_ERROR; + } + goto inf_leave; + + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + + default: /* can't happen, but makes compilers happy */ + ret = Z_STREAM_ERROR; + goto inf_leave; + } + + /* Return unused input */ + inf_leave: + strm->next_in = next; + strm->avail_in = have; + return ret; +} + +int ZEXPORT inflateBackEnd(strm) +z_streamp strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} diff --git a/src/lib/zlib/inffast.c b/src/lib/zlib/inffast.c new file mode 100644 index 00000000000..bbee92ed1e6 --- /dev/null +++ b/src/lib/zlib/inffast.c @@ -0,0 +1,318 @@ +/* inffast.c -- fast decoding + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifndef ASMINF + +/* Allow machine dependent optimization for post-increment or pre-increment. + Based on testing to date, + Pre-increment preferred for: + - PowerPC G3 (Adler) + - MIPS R5000 (Randers-Pehrson) + Post-increment preferred for: + - none + No measurable difference: + - Pentium III (Anderson) + - M68060 (Nikl) + */ +#ifdef POSTINC +# define OFF 0 +# define PUP(a) *(a)++ +#else +# define OFF 1 +# define PUP(a) *++(a) +#endif + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state->mode == LEN + strm->avail_in >= 6 + strm->avail_out >= 258 + start >= strm->avail_out + state->bits < 8 + + On return, state->mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm->avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm->avail_out >= 258 for each loop to avoid checking for + output space. + */ +void inflate_fast(strm, start) +z_streamp strm; +unsigned start; /* inflate()'s starting value for strm->avail_out */ +{ + struct inflate_state FAR *state; + unsigned char FAR *in; /* local strm->next_in */ + unsigned char FAR *last; /* while in < last, enough input available */ + unsigned char FAR *out; /* local strm->next_out */ + unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ + unsigned char FAR *end; /* while out < end, enough space available */ +#ifdef INFLATE_STRICT + unsigned dmax; /* maximum distance from zlib header */ +#endif + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if wsize != 0 */ + unsigned long hold; /* local strm->hold */ + unsigned bits; /* local strm->bits */ + code const FAR *lcode; /* local strm->lencode */ + code const FAR *dcode; /* local strm->distcode */ + unsigned lmask; /* mask for first level of length codes */ + unsigned dmask; /* mask for first level of distance codes */ + code this; /* retrieved table entry */ + unsigned op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + unsigned len; /* match length, unused bytes */ + unsigned dist; /* match distance */ + unsigned char FAR *from; /* where to copy match from */ + + /* copy state to local variables */ + state = (struct inflate_state FAR *)strm->state; + in = strm->next_in - OFF; + last = in + (strm->avail_in - 5); + out = strm->next_out - OFF; + beg = out - (start - strm->avail_out); + end = out + (strm->avail_out - 257); +#ifdef INFLATE_STRICT + dmax = state->dmax; +#endif + wsize = state->wsize; + whave = state->whave; + write = state->write; + window = state->window; + hold = state->hold; + bits = state->bits; + lcode = state->lencode; + dcode = state->distcode; + lmask = (1U << state->lenbits) - 1; + dmask = (1U << state->distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + do { + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = lcode[hold & lmask]; + dolen: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op == 0) { /* literal */ + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + PUP(out) = (unsigned char)(this.val); + } + else if (op & 16) { /* length base */ + len = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + len += (unsigned)hold & ((1U << op) - 1); + hold >>= op; + bits -= op; + } + Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + this = dcode[hold & dmask]; + dodist: + op = (unsigned)(this.bits); + hold >>= op; + bits -= op; + op = (unsigned)(this.op); + if (op & 16) { /* distance base */ + dist = (unsigned)(this.val); + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + if (bits < op) { + hold += (unsigned long)(PUP(in)) << bits; + bits += 8; + } + } + dist += (unsigned)hold & ((1U << op) - 1); +#ifdef INFLATE_STRICT + if (dist > dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + hold >>= op; + bits -= op; + Tracevv((stderr, "inflate: distance %u\n", dist)); + op = (unsigned)(out - beg); /* max distance in output */ + if (dist > op) { /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + from = window - OFF; + if (write == 0) { /* very common case */ + from += wsize - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + else if (write < op) { /* wrap around window */ + from += wsize + write - op; + op -= write; + if (op < len) { /* some from end of window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = window - OFF; + if (write < len) { /* some from start of window */ + op = write; + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + } + else { /* contiguous in window */ + from += write - op; + if (op < len) { /* some from window */ + len -= op; + do { + PUP(out) = PUP(from); + } while (--op); + from = out - dist; /* rest from output */ + } + } + while (len > 2) { + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + else { + from = out - dist; /* copy direct from output */ + do { /* minimum length is three */ + PUP(out) = PUP(from); + PUP(out) = PUP(from); + PUP(out) = PUP(from); + len -= 3; + } while (len > 2); + if (len) { + PUP(out) = PUP(from); + if (len > 1) + PUP(out) = PUP(from); + } + } + } + else if ((op & 64) == 0) { /* 2nd level distance code */ + this = dcode[this.val + (hold & ((1U << op) - 1))]; + goto dodist; + } + else { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + } + else if ((op & 64) == 0) { /* 2nd level length code */ + this = lcode[this.val + (hold & ((1U << op) - 1))]; + goto dolen; + } + else if (op & 32) { /* end-of-block */ + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + else { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + } while (in < last && out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + in -= len; + bits -= len << 3; + hold &= (1U << bits) - 1; + + /* update state and return */ + strm->next_in = in + OFF; + strm->next_out = out + OFF; + strm->avail_in = (unsigned)(in < last ? 5 + (last - in) : 5 - (in - last)); + strm->avail_out = (unsigned)(out < end ? + 257 + (end - out) : 257 - (out - end)); + state->hold = hold; + state->bits = bits; + return; +} + +/* + inflate_fast() speedups that turned out slower (on a PowerPC G3 750CXe): + - Using bit fields for code structure + - Different op definition to avoid & for extra bits (do & for table bits) + - Three separate decoding do-loops for direct, window, and write == 0 + - Special case for distance > 1 copies to do overlapped load and store copy + - Explicit branch predictions (based on measured branch probabilities) + - Deferring match copy and interspersed it with decoding subsequent codes + - Swapping literal/length else + - Swapping window/direct else + - Larger unrolled copy loops (three is about right) + - Moving len -= 3 statement into middle of loop + */ + +#endif /* !ASMINF */ diff --git a/src/lib/zlib/inffast.h b/src/lib/zlib/inffast.h new file mode 100644 index 00000000000..1e88d2d97b5 --- /dev/null +++ b/src/lib/zlib/inffast.h @@ -0,0 +1,11 @@ +/* inffast.h -- header to use inffast.c + * Copyright (C) 1995-2003 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +void inflate_fast OF((z_streamp strm, unsigned start)); diff --git a/src/lib/zlib/inffixed.h b/src/lib/zlib/inffixed.h new file mode 100644 index 00000000000..75ed4b5978d --- /dev/null +++ b/src/lib/zlib/inffixed.h @@ -0,0 +1,94 @@ + /* inffixed.h -- table for decoding fixed codes + * Generated automatically by makefixed(). + */ + + /* WARNING: this file should *not* be used by applications. It + is part of the implementation of the compression library and + is subject to change. Applications should only use zlib.h. + */ + + static const code lenfix[512] = { + {96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, + {0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, + {0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, + {0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, + {0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, + {21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, + {0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, + {0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, + {18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, + {0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, + {0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, + {0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, + {20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, + {0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, + {0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, + {0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, + {16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, + {0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, + {0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, + {0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, + {0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, + {0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, + {0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, + {0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, + {17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, + {0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, + {0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, + {0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, + {19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, + {0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, + {0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, + {0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, + {16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, + {0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, + {0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, + {0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, + {0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, + {20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, + {0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, + {0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, + {17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, + {0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, + {0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, + {0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, + {20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, + {0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, + {0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, + {0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, + {16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, + {0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, + {0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, + {0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, + {0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, + {0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, + {0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, + {0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, + {16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, + {0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, + {0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, + {0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, + {19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, + {0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, + {0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, + {0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, + {16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, + {0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, + {0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, + {0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, + {0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, + {64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, + {0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, + {0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, + {18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, + {0,9,255} + }; + + static const code distfix[32] = { + {16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, + {21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, + {18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, + {19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, + {16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, + {22,5,193},{64,5,0} + }; diff --git a/src/lib/zlib/inflate.c b/src/lib/zlib/inflate.c new file mode 100644 index 00000000000..792fdee8e9c --- /dev/null +++ b/src/lib/zlib/inflate.c @@ -0,0 +1,1368 @@ +/* inflate.c -- zlib decompression + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * Change history: + * + * 1.2.beta0 24 Nov 2002 + * - First version -- complete rewrite of inflate to simplify code, avoid + * creation of window when not needed, minimize use of window when it is + * needed, make inffast.c even faster, implement gzip decoding, and to + * improve code readability and style over the previous zlib inflate code + * + * 1.2.beta1 25 Nov 2002 + * - Use pointers for available input and output checking in inffast.c + * - Remove input and output counters in inffast.c + * - Change inffast.c entry and loop from avail_in >= 7 to >= 6 + * - Remove unnecessary second byte pull from length extra in inffast.c + * - Unroll direct copy to three copies per loop in inffast.c + * + * 1.2.beta2 4 Dec 2002 + * - Change external routine names to reduce potential conflicts + * - Correct filename to inffixed.h for fixed tables in inflate.c + * - Make hbuf[] unsigned char to match parameter type in inflate.c + * - Change strm->next_out[-state->offset] to *(strm->next_out - state->offset) + * to avoid negation problem on Alphas (64 bit) in inflate.c + * + * 1.2.beta3 22 Dec 2002 + * - Add comments on state->bits assertion in inffast.c + * - Add comments on op field in inftrees.h + * - Fix bug in reuse of allocated window after inflateReset() + * - Remove bit fields--back to byte structure for speed + * - Remove distance extra == 0 check in inflate_fast()--only helps for lengths + * - Change post-increments to pre-increments in inflate_fast(), PPC biased? + * - Add compile time option, POSTINC, to use post-increments instead (Intel?) + * - Make MATCH copy in inflate() much faster for when inflate_fast() not used + * - Use local copies of stream next and avail values, as well as local bit + * buffer and bit count in inflate()--for speed when inflate_fast() not used + * + * 1.2.beta4 1 Jan 2003 + * - Split ptr - 257 statements in inflate_table() to avoid compiler warnings + * - Move a comment on output buffer sizes from inffast.c to inflate.c + * - Add comments in inffast.c to introduce the inflate_fast() routine + * - Rearrange window copies in inflate_fast() for speed and simplification + * - Unroll last copy for window match in inflate_fast() + * - Use local copies of window variables in inflate_fast() for speed + * - Pull out common write == 0 case for speed in inflate_fast() + * - Make op and len in inflate_fast() unsigned for consistency + * - Add FAR to lcode and dcode declarations in inflate_fast() + * - Simplified bad distance check in inflate_fast() + * - Added inflateBackInit(), inflateBack(), and inflateBackEnd() in new + * source file infback.c to provide a call-back interface to inflate for + * programs like gzip and unzip -- uses window as output buffer to avoid + * window copying + * + * 1.2.beta5 1 Jan 2003 + * - Improved inflateBack() interface to allow the caller to provide initial + * input in strm. + * - Fixed stored blocks bug in inflateBack() + * + * 1.2.beta6 4 Jan 2003 + * - Added comments in inffast.c on effectiveness of POSTINC + * - Typecasting all around to reduce compiler warnings + * - Changed loops from while (1) or do {} while (1) to for (;;), again to + * make compilers happy + * - Changed type of window in inflateBackInit() to unsigned char * + * + * 1.2.beta7 27 Jan 2003 + * - Changed many types to unsigned or unsigned short to avoid warnings + * - Added inflateCopy() function + * + * 1.2.0 9 Mar 2003 + * - Changed inflateBack() interface to provide separate opaque descriptors + * for the in() and out() functions + * - Changed inflateBack() argument and in_func typedef to swap the length + * and buffer address return values for the input function + * - Check next_in and next_out for Z_NULL on entry to inflate() + * + * The history for versions after 1.2.0 are in ChangeLog in zlib distribution. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +#ifdef MAKEFIXED +# ifndef BUILDFIXED +# define BUILDFIXED +# endif +#endif + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); +local int updatewindow OF((z_streamp strm, unsigned out)); +#ifdef BUILDFIXED + void makefixed OF((void)); +#endif +local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf, + unsigned len)); + +int ZEXPORT inflateReset(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + strm->total_in = strm->total_out = state->total = 0; + strm->msg = Z_NULL; + strm->adler = 1; /* to support ill-conceived Java test suite */ + state->mode = HEAD; + state->last = 0; + state->havedict = 0; + state->dmax = 32768U; + state->head = Z_NULL; + state->wsize = 0; + state->whave = 0; + state->write = 0; + state->hold = 0; + state->bits = 0; + state->lencode = state->distcode = state->next = state->codes; + Tracev((stderr, "inflate: reset\n")); + return Z_OK; +} + +int ZEXPORT inflatePrime(strm, bits, value) +z_streamp strm; +int bits; +int value; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (bits > 16 || state->bits + bits > 32) return Z_STREAM_ERROR; + value &= (1L << bits) - 1; + state->hold += value << state->bits; + state->bits += bits; + return Z_OK; +} + +int ZEXPORT inflateInit2_(strm, windowBits, version, stream_size) +z_streamp strm; +int windowBits; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL) return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; + } + if (strm->zfree == (free_func)0) strm->zfree = zcfree; + state = (struct inflate_state FAR *) + ZALLOC(strm, 1, sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + if (windowBits < 0) { + state->wrap = 0; + windowBits = -windowBits; + } + else { + state->wrap = (windowBits >> 4) + 1; +#ifdef GUNZIP + if (windowBits < 48) windowBits &= 15; +#endif + } + if (windowBits < 8 || windowBits > 15) { + ZFREE(strm, state); + strm->state = Z_NULL; + return Z_STREAM_ERROR; + } + state->wbits = (unsigned)windowBits; + state->window = Z_NULL; + return inflateReset(strm); +} + +int ZEXPORT inflateInit_(strm, version, stream_size) +z_streamp strm; +const char *version; +int stream_size; +{ + return inflateInit2_(strm, DEF_WBITS, version, stream_size); +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +#ifdef MAKEFIXED +#include <stdio.h> + +/* + Write out the inffixed.h that is #include'd above. Defining MAKEFIXED also + defines BUILDFIXED, so the tables are built on the fly. makefixed() writes + those tables to stdout, which would be piped to inffixed.h. A small program + can simply call makefixed to do this: + + void makefixed(void); + + int main(void) + { + makefixed(); + return 0; + } + + Then that can be linked with zlib built with MAKEFIXED defined and run: + + a.out > inffixed.h + */ +void makefixed() +{ + unsigned low, size; + struct inflate_state state; + + fixedtables(&state); + puts(" /* inffixed.h -- table for decoding fixed codes"); + puts(" * Generated automatically by makefixed()."); + puts(" */"); + puts(""); + puts(" /* WARNING: this file should *not* be used by applications."); + puts(" It is part of the implementation of this library and is"); + puts(" subject to change. Applications should only use zlib.h."); + puts(" */"); + puts(""); + size = 1U << 9; + printf(" static const code lenfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 7) == 0) printf("\n "); + printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits, + state.lencode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); + size = 1U << 5; + printf("\n static const code distfix[%u] = {", size); + low = 0; + for (;;) { + if ((low % 6) == 0) printf("\n "); + printf("{%u,%u,%d}", state.distcode[low].op, state.distcode[low].bits, + state.distcode[low].val); + if (++low == size) break; + putchar(','); + } + puts("\n };"); +} +#endif /* MAKEFIXED */ + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +local int updatewindow(strm, out) +z_streamp strm; +unsigned out; +{ + struct inflate_state FAR *state; + unsigned copy, dist; + + state = (struct inflate_state FAR *)strm->state; + + /* if it hasn't been done already, allocate space for the window */ + if (state->window == Z_NULL) { + state->window = (unsigned char FAR *) + ZALLOC(strm, 1U << state->wbits, + sizeof(unsigned char)); + if (state->window == Z_NULL) return 1; + } + + /* if window not in use yet, initialize */ + if (state->wsize == 0) { + state->wsize = 1U << state->wbits; + state->write = 0; + state->whave = 0; + } + + /* copy state->wsize or less output bytes into the circular window */ + copy = out - strm->avail_out; + if (copy >= state->wsize) { + zmemcpy(state->window, strm->next_out - state->wsize, state->wsize); + state->write = 0; + state->whave = state->wsize; + } + else { + dist = state->wsize - state->write; + if (dist > copy) dist = copy; + zmemcpy(state->window + state->write, strm->next_out - copy, dist); + copy -= dist; + if (copy) { + zmemcpy(state->window, strm->next_out - copy, copy); + state->write = copy; + state->whave = state->wsize; + } + else { + state->write += dist; + if (state->write == state->wsize) state->write = 0; + if (state->whave < state->wsize) state->whave += dist; + } + } + return 0; +} + +/* Macros for inflate(): */ + +/* check function to use adler32() for zlib or crc32() for gzip */ +#ifdef GUNZIP +# define UPDATE(check, buf, len) \ + (state->flags ? crc32(check, buf, len) : adler32(check, buf, len)) +#else +# define UPDATE(check, buf, len) adler32(check, buf, len) +#endif + +/* check macros for header crc */ +#ifdef GUNZIP +# define CRC2(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + check = crc32(check, hbuf, 2); \ + } while (0) + +# define CRC4(check, word) \ + do { \ + hbuf[0] = (unsigned char)(word); \ + hbuf[1] = (unsigned char)((word) >> 8); \ + hbuf[2] = (unsigned char)((word) >> 16); \ + hbuf[3] = (unsigned char)((word) >> 24); \ + check = crc32(check, hbuf, 4); \ + } while (0) +#endif + +/* Load registers with state in inflate() for speed */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Restore state from registers in inflate() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflate() + if there is no input available. */ +#define PULLBYTE() \ + do { \ + if (have == 0) goto inf_leave; \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflate(). */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Reverse the bytes in a 32-bit value */ +#define REVERSE(q) \ + ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + +/* + inflate() uses a state machine to process as much input data and generate as + much output data as possible before returning. The state machine is + structured roughly as follows: + + for (;;) switch (state) { + ... + case STATEn: + if (not enough input data or output space to make progress) + return; + ... make progress ... + state = STATEm; + break; + ... + } + + so when inflate() is called again, the same case is attempted again, and + if the appropriate resources are provided, the machine proceeds to the + next state. The NEEDBITS() macro is usually the way the state evaluates + whether it can proceed or should return. NEEDBITS() does the return if + the requested bits are not available. The typical use of the BITS macros + is: + + NEEDBITS(n); + ... do something with BITS(n) ... + DROPBITS(n); + + where NEEDBITS(n) either returns from inflate() if there isn't enough + input left to load n bits into the accumulator, or it continues. BITS(n) + gives the low n bits in the accumulator. When done, DROPBITS(n) drops + the low n bits off the accumulator. INITBITS() clears the accumulator + and sets the number of available bits to zero. BYTEBITS() discards just + enough bits to put the accumulator on a byte boundary. After BYTEBITS() + and a NEEDBITS(8), then BITS(8) would return the next byte in the stream. + + NEEDBITS(n) uses PULLBYTE() to get an available byte of input, or to return + if there is no input available. The decoding of variable length codes uses + PULLBYTE() directly in order to pull just enough bytes to decode the next + code, and no more. + + Some states loop until they get enough input, making sure that enough + state information is maintained to continue the loop where it left off + if NEEDBITS() returns in the loop. For example, want, need, and keep + would all have to actually be part of the saved state in case NEEDBITS() + returns: + + case STATEw: + while (want < need) { + NEEDBITS(n); + keep[want++] = BITS(n); + DROPBITS(n); + } + state = STATEx; + case STATEx: + + As shown above, if the next state is also the next case, then the break + is omitted. + + A state may also return if there is not enough output space available to + complete that state. Those states are copying stored data, writing a + literal byte, and copying a matching string. + + When returning, a "goto inf_leave" is used to update the total counters, + update the check value, and determine whether any progress has been made + during that inflate() call in order to return the proper return code. + Progress is defined as a change in either strm->avail_in or strm->avail_out. + When there is a window, goto inf_leave will update the window with the last + output written. If a goto inf_leave occurs in the middle of decompression + and there is no window currently, goto inf_leave will create one and copy + output to the window for the next call of inflate(). + + In this implementation, the flush parameter of inflate() only affects the + return code (per zlib.h). inflate() always writes as much as possible to + strm->next_out, given the space available and the provided input--the effect + documented in zlib.h of Z_SYNC_FLUSH. Furthermore, inflate() always defers + the allocation of and copying into a sliding window until necessary, which + provides the effect documented in zlib.h for Z_FINISH when the entire input + stream available. So the only thing the flush parameter actually does is: + when flush is set to Z_FINISH, inflate() cannot return Z_OK. Instead it + will return Z_BUF_ERROR if it has not reached the end of the stream. + */ + +int ZEXPORT inflate(strm, flush) +z_streamp strm; +int flush; +{ + struct inflate_state FAR *state; + unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned in, out; /* save starting available input and output */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code this; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ +#ifdef GUNZIP + unsigned char hbuf[4]; /* buffer for gzip header crc calculation */ +#endif + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + if (strm == Z_NULL || strm->state == Z_NULL || strm->next_out == Z_NULL || + (strm->next_in == Z_NULL && strm->avail_in != 0)) + return Z_STREAM_ERROR; + + state = (struct inflate_state FAR *)strm->state; + if (state->mode == TYPE) state->mode = TYPEDO; /* skip check */ + LOAD(); + in = have; + out = left; + ret = Z_OK; + for (;;) + switch (state->mode) { + case HEAD: + if (state->wrap == 0) { + state->mode = TYPEDO; + break; + } + NEEDBITS(16); +#ifdef GUNZIP + if ((state->wrap & 2) && hold == 0x8b1f) { /* gzip header */ + state->check = crc32(0L, Z_NULL, 0); + CRC2(state->check, hold); + INITBITS(); + state->mode = FLAGS; + break; + } + state->flags = 0; /* expect zlib header */ + if (state->head != Z_NULL) + state->head->done = -1; + if (!(state->wrap & 1) || /* check if zlib header allowed */ +#else + if ( +#endif + ((BITS(8) << 8) + (hold >> 8)) % 31) { + strm->msg = (char *)"incorrect header check"; + state->mode = BAD; + break; + } + if (BITS(4) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + DROPBITS(4); + len = BITS(4) + 8; + if (len > state->wbits) { + strm->msg = (char *)"invalid window size"; + state->mode = BAD; + break; + } + state->dmax = 1U << len; + Tracev((stderr, "inflate: zlib header ok\n")); + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = hold & 0x200 ? DICTID : TYPE; + INITBITS(); + break; +#ifdef GUNZIP + case FLAGS: + NEEDBITS(16); + state->flags = (int)(hold); + if ((state->flags & 0xff) != Z_DEFLATED) { + strm->msg = (char *)"unknown compression method"; + state->mode = BAD; + break; + } + if (state->flags & 0xe000) { + strm->msg = (char *)"unknown header flags set"; + state->mode = BAD; + break; + } + if (state->head != Z_NULL) + state->head->text = (int)((hold >> 8) & 1); + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = TIME; + case TIME: + NEEDBITS(32); + if (state->head != Z_NULL) + state->head->time = hold; + if (state->flags & 0x0200) CRC4(state->check, hold); + INITBITS(); + state->mode = OS; + case OS: + NEEDBITS(16); + if (state->head != Z_NULL) { + state->head->xflags = (int)(hold & 0xff); + state->head->os = (int)(hold >> 8); + } + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + state->mode = EXLEN; + case EXLEN: + if (state->flags & 0x0400) { + NEEDBITS(16); + state->length = (unsigned)(hold); + if (state->head != Z_NULL) + state->head->extra_len = (unsigned)hold; + if (state->flags & 0x0200) CRC2(state->check, hold); + INITBITS(); + } + else if (state->head != Z_NULL) + state->head->extra = Z_NULL; + state->mode = EXTRA; + case EXTRA: + if (state->flags & 0x0400) { + copy = state->length; + if (copy > have) copy = have; + if (copy) { + if (state->head != Z_NULL && + state->head->extra != Z_NULL) { + len = state->head->extra_len - state->length; + zmemcpy(state->head->extra + len, next, + len + copy > state->head->extra_max ? + state->head->extra_max - len : copy); + } + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + state->length -= copy; + } + if (state->length) goto inf_leave; + } + state->length = 0; + state->mode = NAME; + case NAME: + if (state->flags & 0x0800) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->name != Z_NULL && + state->length < state->head->name_max) + state->head->name[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->name = Z_NULL; + state->length = 0; + state->mode = COMMENT; + case COMMENT: + if (state->flags & 0x1000) { + if (have == 0) goto inf_leave; + copy = 0; + do { + len = (unsigned)(next[copy++]); + if (state->head != Z_NULL && + state->head->comment != Z_NULL && + state->length < state->head->comm_max) + state->head->comment[state->length++] = len; + } while (len && copy < have); + if (state->flags & 0x0200) + state->check = crc32(state->check, next, copy); + have -= copy; + next += copy; + if (len) goto inf_leave; + } + else if (state->head != Z_NULL) + state->head->comment = Z_NULL; + state->mode = HCRC; + case HCRC: + if (state->flags & 0x0200) { + NEEDBITS(16); + if (hold != (state->check & 0xffff)) { + strm->msg = (char *)"header crc mismatch"; + state->mode = BAD; + break; + } + INITBITS(); + } + if (state->head != Z_NULL) { + state->head->hcrc = (int)((state->flags >> 9) & 1); + state->head->done = 1; + } + strm->adler = state->check = crc32(0L, Z_NULL, 0); + state->mode = TYPE; + break; +#endif + case DICTID: + NEEDBITS(32); + strm->adler = state->check = REVERSE(hold); + INITBITS(); + state->mode = DICT; + case DICT: + if (state->havedict == 0) { + RESTORE(); + return Z_NEED_DICT; + } + strm->adler = state->check = adler32(0L, Z_NULL, 0); + state->mode = TYPE; + case TYPE: + if (flush == Z_BLOCK) goto inf_leave; + case TYPEDO: + if (state->last) { + BYTEBITS(); + state->mode = CHECK; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + case STORED: + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + state->mode = COPY; + case COPY: + copy = state->length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy == 0) goto inf_leave; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + break; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + case TABLE: + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + state->have = 0; + state->mode = LENLENS; + case LENLENS: + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + state->have = 0; + state->mode = CODELENS; + case CODELENS: + while (state->have < state->nlen + state->ndist) { + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.val < 16) { + NEEDBITS(this.bits); + DROPBITS(this.bits); + state->lens[state->have++] = this.val; + } + else { + if (this.val == 16) { + NEEDBITS(this.bits + 2); + DROPBITS(this.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = state->lens[state->have - 1]; + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (this.val == 17) { + NEEDBITS(this.bits + 3); + DROPBITS(this.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(this.bits + 7); + DROPBITS(this.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* build code tables */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + case LEN: + if (have >= 6 && left >= 258) { + RESTORE(); + inflate_fast(strm, out); + LOAD(); + break; + } + for (;;) { + this = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if (this.op && (this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + state->length = (unsigned)this.val; + if ((int)(this.op) == 0) { + Tracevv((stderr, this.val >= 0x20 && this.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", this.val)); + state->mode = LIT; + break; + } + if (this.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + if (this.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + state->extra = (unsigned)(this.op) & 15; + state->mode = LENEXT; + case LENEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + state->mode = DIST; + case DIST: + for (;;) { + this = state->distcode[BITS(state->distbits)]; + if ((unsigned)(this.bits) <= bits) break; + PULLBYTE(); + } + if ((this.op & 0xf0) == 0) { + last = this; + for (;;) { + this = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + this.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(this.bits); + if (this.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)this.val; + state->extra = (unsigned)(this.op) & 15; + state->mode = DISTEXT; + case DISTEXT: + if (state->extra) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } +#ifdef INFLATE_STRICT + if (state->offset > state->dmax) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } +#endif + if (state->offset > state->whave + out - left) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + state->mode = MATCH; + case MATCH: + if (left == 0) goto inf_leave; + copy = out - left; + if (state->offset > copy) { /* copy from window */ + copy = state->offset - copy; + if (copy > state->write) { + copy -= state->write; + from = state->window + (state->wsize - copy); + } + else + from = state->window + (state->write - copy); + if (copy > state->length) copy = state->length; + } + else { /* copy from output */ + from = put - state->offset; + copy = state->length; + } + if (copy > left) copy = left; + left -= copy; + state->length -= copy; + do { + *put++ = *from++; + } while (--copy); + if (state->length == 0) state->mode = LEN; + break; + case LIT: + if (left == 0) goto inf_leave; + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + case CHECK: + if (state->wrap) { + NEEDBITS(32); + out -= left; + strm->total_out += out; + state->total += out; + if (out) + strm->adler = state->check = + UPDATE(state->check, put - out, out); + out = left; + if (( +#ifdef GUNZIP + state->flags ? hold : +#endif + REVERSE(hold)) != state->check) { + strm->msg = (char *)"incorrect data check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: check matches trailer\n")); + } +#ifdef GUNZIP + state->mode = LENGTH; + case LENGTH: + if (state->wrap && state->flags) { + NEEDBITS(32); + if (hold != (state->total & 0xffffffffUL)) { + strm->msg = (char *)"incorrect length check"; + state->mode = BAD; + break; + } + INITBITS(); + Tracev((stderr, "inflate: length matches trailer\n")); + } +#endif + state->mode = DONE; + case DONE: + ret = Z_STREAM_END; + goto inf_leave; + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + case MEM: + return Z_MEM_ERROR; + case SYNC: + default: + return Z_STREAM_ERROR; + } + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + inf_leave: + RESTORE(); + if (state->wsize || (state->mode < CHECK && out != strm->avail_out)) + if (updatewindow(strm, out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + in -= strm->avail_in; + out -= strm->avail_out; + strm->total_in += in; + strm->total_out += out; + state->total += out; + if (state->wrap && out) + strm->adler = state->check = + UPDATE(state->check, strm->next_out - out, out); + strm->data_type = state->bits + (state->last ? 64 : 0) + + (state->mode == TYPE ? 128 : 0); + if (((in == 0 && out == 0) || flush == Z_FINISH) && ret == Z_OK) + ret = Z_BUF_ERROR; + return ret; +} + +int ZEXPORT inflateEnd(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->window != Z_NULL) ZFREE(strm, state->window); + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} + +int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) +z_streamp strm; +const Bytef *dictionary; +uInt dictLength; +{ + struct inflate_state FAR *state; + unsigned long id; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (state->wrap != 0 && state->mode != DICT) + return Z_STREAM_ERROR; + + /* check for correct dictionary id */ + if (state->mode == DICT) { + id = adler32(0L, Z_NULL, 0); + id = adler32(id, dictionary, dictLength); + if (id != state->check) + return Z_DATA_ERROR; + } + + /* copy dictionary to window */ + if (updatewindow(strm, strm->avail_out)) { + state->mode = MEM; + return Z_MEM_ERROR; + } + if (dictLength > state->wsize) { + zmemcpy(state->window, dictionary + dictLength - state->wsize, + state->wsize); + state->whave = state->wsize; + } + else { + zmemcpy(state->window + state->wsize - dictLength, dictionary, + dictLength); + state->whave = dictLength; + } + state->havedict = 1; + Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK; +} + +int ZEXPORT inflateGetHeader(strm, head) +z_streamp strm; +gz_headerp head; +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if ((state->wrap & 2) == 0) return Z_STREAM_ERROR; + + /* save header structure */ + state->head = head; + head->done = 0; + return Z_OK; +} + +/* + Search buf[0..len-1] for the pattern: 0, 0, 0xff, 0xff. Return when found + or when out of input. When called, *have is the number of pattern bytes + found in order so far, in 0..3. On return *have is updated to the new + state. If on return *have equals four, then the pattern was found and the + return value is how many bytes were read including the last byte of the + pattern. If *have is less than four, then the pattern has not been found + yet and the return value is len. In the latter case, syncsearch() can be + called again with more data and the *have state. *have is initialized to + zero for the first call. + */ +local unsigned syncsearch(have, buf, len) +unsigned FAR *have; +unsigned char FAR *buf; +unsigned len; +{ + unsigned got; + unsigned next; + + got = *have; + next = 0; + while (next < len && got < 4) { + if ((int)(buf[next]) == (got < 2 ? 0 : 0xff)) + got++; + else if (buf[next]) + got = 0; + else + got = 4 - got; + next++; + } + *have = got; + return next; +} + +int ZEXPORT inflateSync(strm) +z_streamp strm; +{ + unsigned len; /* number of bytes to look at or looked at */ + unsigned long in, out; /* temporary to save total_in and total_out */ + unsigned char buf[4]; /* to restore bit buffer to byte string */ + struct inflate_state FAR *state; + + /* check parameters */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + if (strm->avail_in == 0 && state->bits < 8) return Z_BUF_ERROR; + + /* if first time, start search in bit buffer */ + if (state->mode != SYNC) { + state->mode = SYNC; + state->hold <<= state->bits & 7; + state->bits -= state->bits & 7; + len = 0; + while (state->bits >= 8) { + buf[len++] = (unsigned char)(state->hold); + state->hold >>= 8; + state->bits -= 8; + } + state->have = 0; + syncsearch(&(state->have), buf, len); + } + + /* search available input */ + len = syncsearch(&(state->have), strm->next_in, strm->avail_in); + strm->avail_in -= len; + strm->next_in += len; + strm->total_in += len; + + /* return no joy or set up to restart inflate() on a new block */ + if (state->have != 4) return Z_DATA_ERROR; + in = strm->total_in; out = strm->total_out; + inflateReset(strm); + strm->total_in = in; strm->total_out = out; + state->mode = TYPE; + return Z_OK; +} + +/* + Returns true if inflate is currently at the end of a block generated by + Z_SYNC_FLUSH or Z_FULL_FLUSH. This function is used by one PPP + implementation to provide an additional safety check. PPP uses + Z_SYNC_FLUSH but removes the length bytes of the resulting empty stored + block. When decompressing, PPP checks that at the end of input packet, + inflate is waiting for these length bytes. + */ +int ZEXPORT inflateSyncPoint(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + return state->mode == STORED && state->bits == 0; +} + +int ZEXPORT inflateCopy(dest, source) +z_streamp dest; +z_streamp source; +{ + struct inflate_state FAR *state; + struct inflate_state FAR *copy; + unsigned char FAR *window; + unsigned wsize; + + /* check input */ + if (dest == Z_NULL || source == Z_NULL || source->state == Z_NULL || + source->zalloc == (alloc_func)0 || source->zfree == (free_func)0) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)source->state; + + /* allocate space */ + copy = (struct inflate_state FAR *) + ZALLOC(source, 1, sizeof(struct inflate_state)); + if (copy == Z_NULL) return Z_MEM_ERROR; + window = Z_NULL; + if (state->window != Z_NULL) { + window = (unsigned char FAR *) + ZALLOC(source, 1U << state->wbits, sizeof(unsigned char)); + if (window == Z_NULL) { + ZFREE(source, copy); + return Z_MEM_ERROR; + } + } + + /* copy state */ + zmemcpy(dest, source, sizeof(z_stream)); + zmemcpy(copy, state, sizeof(struct inflate_state)); + if (state->lencode >= state->codes && + state->lencode <= state->codes + ENOUGH - 1) { + copy->lencode = copy->codes + (state->lencode - state->codes); + copy->distcode = copy->codes + (state->distcode - state->codes); + } + copy->next = copy->codes + (state->next - state->codes); + if (window != Z_NULL) { + wsize = 1U << state->wbits; + zmemcpy(window, state->window, wsize); + } + copy->window = window; + dest->state = (struct internal_state FAR *)copy; + return Z_OK; +} diff --git a/src/lib/zlib/inflate.h b/src/lib/zlib/inflate.h new file mode 100644 index 00000000000..07bd3e78a7c --- /dev/null +++ b/src/lib/zlib/inflate.h @@ -0,0 +1,115 @@ +/* inflate.h -- internal inflate state definition + * Copyright (C) 1995-2004 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* define NO_GZIP when compiling if you want to disable gzip header and + trailer decoding by inflate(). NO_GZIP would be used to avoid linking in + the crc code when it is not needed. For shared libraries, gzip decoding + should be left enabled. */ +#ifndef NO_GZIP +# define GUNZIP +#endif + +/* Possible inflate modes between inflate() calls */ +typedef enum { + HEAD, /* i: waiting for magic header */ + FLAGS, /* i: waiting for method and flags (gzip) */ + TIME, /* i: waiting for modification time (gzip) */ + OS, /* i: waiting for extra flags and operating system (gzip) */ + EXLEN, /* i: waiting for extra length (gzip) */ + EXTRA, /* i: waiting for extra bytes (gzip) */ + NAME, /* i: waiting for end of file name (gzip) */ + COMMENT, /* i: waiting for end of comment (gzip) */ + HCRC, /* i: waiting for header crc (gzip) */ + DICTID, /* i: waiting for dictionary check value */ + DICT, /* waiting for inflateSetDictionary() call */ + TYPE, /* i: waiting for type bits, including last-flag bit */ + TYPEDO, /* i: same, but skip check to exit inflate on new block */ + STORED, /* i: waiting for stored size (length and complement) */ + COPY, /* i/o: waiting for input or output to copy stored block */ + TABLE, /* i: waiting for dynamic block table lengths */ + LENLENS, /* i: waiting for code length code lengths */ + CODELENS, /* i: waiting for length/lit and distance code lengths */ + LEN, /* i: waiting for length/lit code */ + LENEXT, /* i: waiting for length extra bits */ + DIST, /* i: waiting for distance code */ + DISTEXT, /* i: waiting for distance extra bits */ + MATCH, /* o: waiting for output space to copy string */ + LIT, /* o: waiting for output space to write literal */ + CHECK, /* i: waiting for 32-bit check value */ + LENGTH, /* i: waiting for 32-bit length (gzip) */ + DONE, /* finished check, done -- remain here until reset */ + BAD, /* got a data error -- remain here until reset */ + MEM, /* got an inflate() memory error -- remain here until reset */ + SYNC /* looking for synchronization bytes to restart inflate() */ +} inflate_mode; + +/* + State transitions between above modes - + + (most modes can go to the BAD or MEM mode -- not shown for clarity) + + Process header: + HEAD -> (gzip) or (zlib) + (gzip) -> FLAGS -> TIME -> OS -> EXLEN -> EXTRA -> NAME + NAME -> COMMENT -> HCRC -> TYPE + (zlib) -> DICTID or TYPE + DICTID -> DICT -> TYPE + Read deflate blocks: + TYPE -> STORED or TABLE or LEN or CHECK + STORED -> COPY -> TYPE + TABLE -> LENLENS -> CODELENS -> LEN + Read deflate codes: + LEN -> LENEXT or LIT or TYPE + LENEXT -> DIST -> DISTEXT -> MATCH -> LEN + LIT -> LEN + Process trailer: + CHECK -> LENGTH -> DONE + */ + +/* state maintained between inflate() calls. Approximately 7K bytes. */ +struct inflate_state { + inflate_mode mode; /* current inflate mode */ + int last; /* true if processing last block */ + int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ + int havedict; /* true if dictionary provided */ + int flags; /* gzip header method and flags (0 if zlib) */ + unsigned dmax; /* zlib header max distance (INFLATE_STRICT) */ + unsigned long check; /* protected copy of check value */ + unsigned long total; /* protected copy of output count */ + gz_headerp head; /* where to save gzip header information */ + /* sliding window */ + unsigned wbits; /* log base 2 of requested window size */ + unsigned wsize; /* window size or zero if not using window */ + unsigned whave; /* valid bytes in the window */ + unsigned write; /* window write index */ + unsigned char FAR *window; /* allocated sliding window, if needed */ + /* bit accumulator */ + unsigned long hold; /* input bit accumulator */ + unsigned bits; /* number of bits in "in" */ + /* for string and stored block copying */ + unsigned length; /* literal or length of data to copy */ + unsigned offset; /* distance back to copy string from */ + /* for table and code decoding */ + unsigned extra; /* extra bits needed */ + /* fixed and dynamic code tables */ + code const FAR *lencode; /* starting table for length/literal codes */ + code const FAR *distcode; /* starting table for distance codes */ + unsigned lenbits; /* index bits for lencode */ + unsigned distbits; /* index bits for distcode */ + /* dynamic table building */ + unsigned ncode; /* number of code length code lengths */ + unsigned nlen; /* number of length code lengths */ + unsigned ndist; /* number of distance code lengths */ + unsigned have; /* number of code lengths in lens[] */ + code FAR *next; /* next available space in codes[] */ + unsigned short lens[320]; /* temporary storage for code lengths */ + unsigned short work[288]; /* work area for code table building */ + code codes[ENOUGH]; /* space for code tables */ +}; diff --git a/src/lib/zlib/inftrees.c b/src/lib/zlib/inftrees.c new file mode 100644 index 00000000000..8a9c13ff03d --- /dev/null +++ b/src/lib/zlib/inftrees.c @@ -0,0 +1,329 @@ +/* inftrees.c -- generate Huffman trees for efficient decoding + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +#include "zutil.h" +#include "inftrees.h" + +#define MAXBITS 15 + +const char inflate_copyright[] = + " inflate 1.2.3 Copyright 1995-2005 Mark Adler "; +/* + If you use the zlib library in a product, an acknowledgment is welcome + in the documentation of your product. If for some reason you cannot + include such an acknowledgment, I would appreciate that you keep this + copyright string in the executable of your product. + */ + +/* + Build a set of tables to decode the provided canonical Huffman code. + The code lengths are lens[0..codes-1]. The result starts at *table, + whose indices are 0..2^bits-1. work is a writable array of at least + lens shorts, which is used as a work area. type is the type of code + to be generated, CODES, LENS, or DISTS. On return, zero is success, + -1 is an invalid code, and +1 means that ENOUGH isn't enough. table + on return points to the next available entry's address. bits is the + requested root table index bits, and on return it is the actual root + table index bits. It will differ if the request is greater than the + longest code or if it is less than the shortest code. + */ +int inflate_table(type, lens, codes, table, bits, work) +codetype type; +unsigned short FAR *lens; +unsigned codes; +code FAR * FAR *table; +unsigned FAR *bits; +unsigned short FAR *work; +{ + unsigned len; /* a code's length in bits */ + unsigned sym; /* index of code symbols */ + unsigned min, max; /* minimum and maximum code lengths */ + unsigned root; /* number of index bits for root table */ + unsigned curr; /* number of index bits for current table */ + unsigned drop; /* code bits to drop for sub-table */ + int left; /* number of prefix codes available */ + unsigned used; /* code entries in table used */ + unsigned huff; /* Huffman code */ + unsigned incr; /* for incrementing code, index */ + unsigned fill; /* index for replicating entries */ + unsigned low; /* low bits for current root entry */ + unsigned mask; /* mask for low root bits */ + code this; /* table entry for duplication */ + code FAR *next; /* next available space in table */ + const unsigned short FAR *base; /* base value table to use */ + const unsigned short FAR *extra; /* extra bits table to use */ + int end; /* use base and extra for symbol > end */ + unsigned short count[MAXBITS+1]; /* number of codes of each length */ + unsigned short offs[MAXBITS+1]; /* offsets in table for each length */ + static const unsigned short lbase[31] = { /* Length codes 257..285 base */ + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const unsigned short lext[31] = { /* Length codes 257..285 extra */ + 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 201, 196}; + static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, + 8193, 12289, 16385, 24577, 0, 0}; + static const unsigned short dext[32] = { /* Distance codes 0..29 extra */ + 16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, + 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, + 28, 28, 29, 29, 64, 64}; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) + count[len] = 0; + for (sym = 0; sym < codes; sym++) + count[lens[sym]]++; + + /* bound code lengths, force root to be within code lengths */ + root = *bits; + for (max = MAXBITS; max >= 1; max--) + if (count[max] != 0) break; + if (root > max) root = max; + if (max == 0) { /* no symbols to code at all */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)1; + this.val = (unsigned short)0; + *(*table)++ = this; /* make a table to force an error */ + *(*table)++ = this; + *bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min <= MAXBITS; min++) + if (count[min] != 0) break; + if (root < min) root = min; + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) return -1; /* over-subscribed */ + } + if (left > 0 && (type == CODES || max != 1)) + return -1; /* incomplete set */ + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) + offs[len + 1] = offs[len] + count[len]; + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) + if (lens[sym] != 0) work[offs[lens[sym]]++] = (unsigned short)sym; + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked when a LENS table is being made + against the space in *table, ENOUGH, minus the maximum space needed by + the worst case distance code, MAXD. This should never happen, but the + sufficiency of ENOUGH has not been proven exhaustively, hence the check. + This assumes that when type == LENS, bits == 9. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + switch (type) { + case CODES: + base = extra = work; /* dummy value--not used */ + end = 19; + break; + case LENS: + base = lbase; + base -= 257; + extra = lext; + extra -= 257; + end = 256; + break; + default: /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize state for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = *table; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = (unsigned)(-1); /* trigger new sub-table when len > root */ + used = 1U << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + this.bits = (unsigned char)(len - drop); + if ((int)(work[sym]) < end) { + this.op = (unsigned char)0; + this.val = work[sym]; + } + else if ((int)(work[sym]) > end) { + this.op = (unsigned char)(extra[work[sym]]); + this.val = base[work[sym]]; + } + else { + this.op = (unsigned char)(32 + 64); /* end of block */ + this.val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1U << (len - drop); + fill = 1U << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + next[(huff >> drop) + fill] = this; + } while (fill != 0); + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + + /* go to next symbol, update count, len */ + sym++; + if (--(count[len]) == 0) { + if (len == max) break; + len = lens[work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) != low) { + /* if first time, transition to sub-tables */ + if (drop == 0) + drop = root; + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = (int)(1 << curr); + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1U << curr; + if (type == LENS && used >= ENOUGH - MAXD) + return 1; + + /* point entry in root table to sub-table */ + low = huff & mask; + (*table)[low].op = (unsigned char)curr; + (*table)[low].bits = (unsigned char)root; + (*table)[low].val = (unsigned short)(next - *table); + } + } + + /* + Fill in rest of table for incomplete codes. This loop is similar to the + loop above in incrementing huff for table indices. It is assumed that + len is equal to curr + drop, so there is no loop needed to increment + through high index bits. When the current sub-table is filled, the loop + drops back to the root table to fill in any remaining entries there. + */ + this.op = (unsigned char)64; /* invalid code marker */ + this.bits = (unsigned char)(len - drop); + this.val = (unsigned short)0; + while (huff != 0) { + /* when done with sub-table, drop back to root table */ + if (drop != 0 && (huff & mask) != low) { + drop = 0; + len = root; + next = *table; + this.bits = (unsigned char)len; + } + + /* put invalid code marker in table */ + next[huff >> drop] = this; + + /* backwards increment the len-bit code huff */ + incr = 1U << (len - 1); + while (huff & incr) + incr >>= 1; + if (incr != 0) { + huff &= incr - 1; + huff += incr; + } + else + huff = 0; + } + + /* set return parameters */ + *table += used; + *bits = root; + return 0; +} diff --git a/src/lib/zlib/inftrees.h b/src/lib/zlib/inftrees.h new file mode 100644 index 00000000000..b1104c87e76 --- /dev/null +++ b/src/lib/zlib/inftrees.h @@ -0,0 +1,55 @@ +/* inftrees.h -- header to use inftrees.c + * Copyright (C) 1995-2005 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* Structure for decoding tables. Each entry provides either the + information needed to do the operation requested by the code that + indexed that table entry, or it provides a pointer to another + table that indexes more bits of the code. op indicates whether + the entry is a pointer to another table, a literal, a length or + distance, an end-of-block, or an invalid code. For a table + pointer, the low four bits of op is the number of index bits of + that table. For a length or distance, the low four bits of op + is the number of extra bits to get after the code. bits is + the number of bits in this code or part of the code to drop off + of the bit buffer. val is the actual byte to output in the case + of a literal, the base length or distance, or the offset from + the current table to the next table. Each entry is four bytes. */ +typedef struct { + unsigned char op; /* operation, extra bits, table bits */ + unsigned char bits; /* bits in this part of the code */ + unsigned short val; /* offset in table or code value */ +} code; + +/* op values as set by inflate_table(): + 00000000 - literal + 0000tttt - table link, tttt != 0 is the number of table index bits + 0001eeee - length or distance, eeee is the number of extra bits + 01100000 - end of block + 01000000 - invalid code + */ + +/* Maximum size of dynamic tree. The maximum found in a long but non- + exhaustive search was 1444 code structures (852 for length/literals + and 592 for distances, the latter actually the result of an + exhaustive search). The true maximum is not known, but the value + below is more than safe. */ +#define ENOUGH 2048 +#define MAXD 592 + +/* Type of code to build for inftable() */ +typedef enum { + CODES, + LENS, + DISTS +} codetype; + +extern int inflate_table OF((codetype type, unsigned short FAR *lens, + unsigned codes, code FAR * FAR *table, + unsigned FAR *bits, unsigned short FAR *work)); diff --git a/src/lib/zlib/minigzip.c b/src/lib/zlib/minigzip.c new file mode 100644 index 00000000000..4524b96a1d6 --- /dev/null +++ b/src/lib/zlib/minigzip.c @@ -0,0 +1,322 @@ +/* minigzip.c -- simulate gzip using the zlib compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * minigzip is a minimal implementation of the gzip utility. This is + * only an example of using zlib and isn't meant to replace the + * full-featured gzip. No attempt is made to deal with file systems + * limiting names to 14 or 8+3 characters, etc... Error checking is + * very limited. So use minigzip only for testing; use gzip for the + * real thing. On MSDOS, use only on file names without extension + * or in pipe mode. + */ + +/* @(#) $Id$ */ + +#include <stdio.h> +#include "zlib.h" + +#ifdef STDC +# include <string.h> +# include <stdlib.h> +#endif + +#ifdef USE_MMAP +# include <sys/types.h> +# include <sys/mman.h> +# include <sys/stat.h> +#endif + +#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__) +# include <fcntl.h> +# include <io.h> +# define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY) +#else +# define SET_BINARY_MODE(file) +#endif + +#ifdef VMS +# define unlink delete +# define GZ_SUFFIX "-gz" +#endif +#ifdef RISCOS +# define unlink remove +# define GZ_SUFFIX "-gz" +# define fileno(file) file->__file +#endif +#if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include <unix.h> /* for fileno */ +#endif + +#ifndef WIN32 /* unlink already in stdio.h for WIN32 */ + extern int unlink OF((const char *)); +#endif + +#ifndef GZ_SUFFIX +# define GZ_SUFFIX ".gz" +#endif +#define SUFFIX_LEN (sizeof(GZ_SUFFIX)-1) + +#define BUFLEN 16384 +#define MAX_NAME_LEN 1024 + +#ifdef MAXSEG_64K +# define local static + /* Needed for systems with limitation on stack size. */ +#else +# define local +#endif + +char *prog; + +void error OF((const char *msg)); +void gz_compress OF((FILE *in, gzFile out)); +#ifdef USE_MMAP +int gz_compress_mmap OF((FILE *in, gzFile out)); +#endif +void gz_uncompress OF((gzFile in, FILE *out)); +void file_compress OF((char *file, char *mode)); +void file_uncompress OF((char *file)); +int main OF((int argc, char *argv[])); + +/* =========================================================================== + * Display error message and exit + */ +void error(msg) + const char *msg; +{ + fprintf(stderr, "%s: %s\n", prog, msg); + exit(1); +} + +/* =========================================================================== + * Compress input to output then close both files. + */ + +void gz_compress(in, out) + FILE *in; + gzFile out; +{ + local char buf[BUFLEN]; + int len; + int err; + +#ifdef USE_MMAP + /* Try first compressing with mmap. If mmap fails (minigzip used in a + * pipe), use the normal fread loop. + */ + if (gz_compress_mmap(in, out) == Z_OK) return; +#endif + for (;;) { + len = (int)fread(buf, 1, sizeof(buf), in); + if (ferror(in)) { + perror("fread"); + exit(1); + } + if (len == 0) break; + + if (gzwrite(out, buf, (unsigned)len) != len) error(gzerror(out, &err)); + } + fclose(in); + if (gzclose(out) != Z_OK) error("failed gzclose"); +} + +#ifdef USE_MMAP /* MMAP version, Miguel Albrecht <malbrech@eso.org> */ + +/* Try compressing the input file at once using mmap. Return Z_OK if + * if success, Z_ERRNO otherwise. + */ +int gz_compress_mmap(in, out) + FILE *in; + gzFile out; +{ + int len; + int err; + int ifd = fileno(in); + caddr_t buf; /* mmap'ed buffer for the entire input file */ + off_t buf_len; /* length of the input file */ + struct stat sb; + + /* Determine the size of the file, needed for mmap: */ + if (fstat(ifd, &sb) < 0) return Z_ERRNO; + buf_len = sb.st_size; + if (buf_len <= 0) return Z_ERRNO; + + /* Now do the actual mmap: */ + buf = mmap((caddr_t) 0, buf_len, PROT_READ, MAP_SHARED, ifd, (off_t)0); + if (buf == (caddr_t)(-1)) return Z_ERRNO; + + /* Compress the whole file at once: */ + len = gzwrite(out, (char *)buf, (unsigned)buf_len); + + if (len != (int)buf_len) error(gzerror(out, &err)); + + munmap(buf, buf_len); + fclose(in); + if (gzclose(out) != Z_OK) error("failed gzclose"); + return Z_OK; +} +#endif /* USE_MMAP */ + +/* =========================================================================== + * Uncompress input to output then close both files. + */ +void gz_uncompress(in, out) + gzFile in; + FILE *out; +{ + local char buf[BUFLEN]; + int len; + int err; + + for (;;) { + len = gzread(in, buf, sizeof(buf)); + if (len < 0) error (gzerror(in, &err)); + if (len == 0) break; + + if ((int)fwrite(buf, 1, (unsigned)len, out) != len) { + error("failed fwrite"); + } + } + if (fclose(out)) error("failed fclose"); + + if (gzclose(in) != Z_OK) error("failed gzclose"); +} + + +/* =========================================================================== + * Compress the given file: create a corresponding .gz file and remove the + * original. + */ +void file_compress(file, mode) + char *file; + char *mode; +{ + local char outfile[MAX_NAME_LEN]; + FILE *in; + gzFile out; + + strcpy(outfile, file); + strcat(outfile, GZ_SUFFIX); + + in = fopen(file, "rb"); + if (in == NULL) { + perror(file); + exit(1); + } + out = gzopen(outfile, mode); + if (out == NULL) { + fprintf(stderr, "%s: can't gzopen %s\n", prog, outfile); + exit(1); + } + gz_compress(in, out); + + unlink(file); +} + + +/* =========================================================================== + * Uncompress the given file and remove the original. + */ +void file_uncompress(file) + char *file; +{ + local char buf[MAX_NAME_LEN]; + char *infile, *outfile; + FILE *out; + gzFile in; + uInt len = (uInt)strlen(file); + + strcpy(buf, file); + + if (len > SUFFIX_LEN && strcmp(file+len-SUFFIX_LEN, GZ_SUFFIX) == 0) { + infile = file; + outfile = buf; + outfile[len-3] = '\0'; + } else { + outfile = file; + infile = buf; + strcat(infile, GZ_SUFFIX); + } + in = gzopen(infile, "rb"); + if (in == NULL) { + fprintf(stderr, "%s: can't gzopen %s\n", prog, infile); + exit(1); + } + out = fopen(outfile, "wb"); + if (out == NULL) { + perror(file); + exit(1); + } + + gz_uncompress(in, out); + + unlink(infile); +} + + +/* =========================================================================== + * Usage: minigzip [-d] [-f] [-h] [-r] [-1 to -9] [files...] + * -d : decompress + * -f : compress with Z_FILTERED + * -h : compress with Z_HUFFMAN_ONLY + * -r : compress with Z_RLE + * -1 to -9 : compression level + */ + +int main(argc, argv) + int argc; + char *argv[]; +{ + int uncompr = 0; + gzFile file; + char outmode[20]; + + strcpy(outmode, "wb6 "); + + prog = argv[0]; + argc--, argv++; + + while (argc > 0) { + if (strcmp(*argv, "-d") == 0) + uncompr = 1; + else if (strcmp(*argv, "-f") == 0) + outmode[3] = 'f'; + else if (strcmp(*argv, "-h") == 0) + outmode[3] = 'h'; + else if (strcmp(*argv, "-r") == 0) + outmode[3] = 'R'; + else if ((*argv)[0] == '-' && (*argv)[1] >= '1' && (*argv)[1] <= '9' && + (*argv)[2] == 0) + outmode[2] = (*argv)[1]; + else + break; + argc--, argv++; + } + if (outmode[3] == ' ') + outmode[3] = 0; + if (argc == 0) { + SET_BINARY_MODE(stdin); + SET_BINARY_MODE(stdout); + if (uncompr) { + file = gzdopen(fileno(stdin), "rb"); + if (file == NULL) error("can't gzdopen stdin"); + gz_uncompress(file, stdout); + } else { + file = gzdopen(fileno(stdout), outmode); + if (file == NULL) error("can't gzdopen stdout"); + gz_compress(stdin, file); + } + } else { + do { + if (uncompr) { + file_uncompress(*argv); + } else { + file_compress(*argv, outmode); + } + } while (argv++, --argc); + } + return 0; +} diff --git a/src/lib/zlib/readme b/src/lib/zlib/readme new file mode 100644 index 00000000000..758cc50020d --- /dev/null +++ b/src/lib/zlib/readme @@ -0,0 +1,125 @@ +ZLIB DATA COMPRESSION LIBRARY + +zlib 1.2.3 is a general purpose data compression library. All the code is +thread safe. The data format used by the zlib library is described by RFCs +(Request for Comments) 1950 to 1952 in the files +http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate format) +and rfc1952.txt (gzip format). These documents are also available in other +formats from ftp://ftp.uu.net/graphics/png/documents/zlib/zdoc-index.html + +All functions of the compression library are documented in the file zlib.h +(volunteer to write man pages welcome, contact zlib@gzip.org). A usage example +of the library is given in the file example.c which also tests that the library +is working correctly. Another example is given in the file minigzip.c. The +compression library itself is composed of all source files except example.c and +minigzip.c. + +To compile all files and run the test program, follow the instructions given at +the top of Makefile. In short "make test; make install" should work for most +machines. For Unix: "./configure; make test; make install". For MSDOS, use one +of the special makefiles such as Makefile.msc. For VMS, use make_vms.com. + +Questions about zlib should be sent to <zlib@gzip.org>, or to Gilles Vollant +<info@winimage.com> for the Windows DLL version. The zlib home page is +http://www.zlib.org or http://www.gzip.org/zlib/ Before reporting a problem, +please check this site to verify that you have the latest version of zlib; +otherwise get the latest version and check whether the problem still exists or +not. + +PLEASE read the zlib FAQ http://www.gzip.org/zlib/zlib_faq.html before asking +for help. + +Mark Nelson <markn@ieee.org> wrote an article about zlib for the Jan. 1997 +issue of Dr. Dobb's Journal; a copy of the article is available in +http://dogma.net/markn/articles/zlibtool/zlibtool.htm + +The changes made in version 1.2.3 are documented in the file ChangeLog. + +Unsupported third party contributions are provided in directory "contrib". + +A Java implementation of zlib is available in the Java Development Kit +http://java.sun.com/j2se/1.4.2/docs/api/java/util/zip/package-summary.html +See the zlib home page http://www.zlib.org for details. + +A Perl interface to zlib written by Paul Marquess <pmqs@cpan.org> is in the +CPAN (Comprehensive Perl Archive Network) sites +http://www.cpan.org/modules/by-module/Compress/ + +A Python interface to zlib written by A.M. Kuchling <amk@amk.ca> is +available in Python 1.5 and later versions, see +http://www.python.org/doc/lib/module-zlib.html + +A zlib binding for TCL written by Andreas Kupries <a.kupries@westend.com> is +availlable at http://www.oche.de/~akupries/soft/trf/trf_zip.html + +An experimental package to read and write files in .zip format, written on top +of zlib by Gilles Vollant <info@winimage.com>, is available in the +contrib/minizip directory of zlib. + + +Notes for some targets: + +- For Windows DLL versions, please see win32/DLL_FAQ.txt + +- For 64-bit Irix, deflate.c must be compiled without any optimization. With + -O, one libpng test fails. The test works in 32 bit mode (with the -n32 + compiler flag). The compiler bug has been reported to SGI. + +- zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1 it works + when compiled with cc. + +- On Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1 is + necessary to get gzprintf working correctly. This is done by configure. + +- zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works with + other compilers. Use "make test" to check your compiler. + +- gzdopen is not supported on RISCOS, BEOS and by some Mac compilers. + +- For PalmOs, see http://palmzlib.sourceforge.net/ + +- When building a shared, i.e. dynamic library on Mac OS X, the library must be + installed before testing (do "make install" before "make test"), since the + library location is specified in the library. + + +Acknowledgments: + + The deflate format used by zlib was defined by Phil Katz. The deflate + and zlib specifications were written by L. Peter Deutsch. Thanks to all the + people who reported problems and suggested various improvements in zlib; + they are too numerous to cite here. + +Copyright notice: + + (C) 1995-2004 Jean-loup Gailly and Mark Adler + + 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. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + +If you use the zlib library in a product, we would appreciate *not* +receiving lengthy legal documents to sign. The sources are provided +for free but without warranty of any kind. The library has been +entirely written by Jean-loup Gailly and Mark Adler; it does not +include third-party code. + +If you redistribute modified sources, we would appreciate that you include +in the file ChangeLog history information documenting your changes. Please +read the FAQ for more information on the distribution of modified source +versions. diff --git a/src/lib/zlib/trees.c b/src/lib/zlib/trees.c new file mode 100644 index 00000000000..395e4e16814 --- /dev/null +++ b/src/lib/zlib/trees.c @@ -0,0 +1,1219 @@ +/* trees.c -- output deflated data using Huffman coding + * Copyright (C) 1995-2005 Jean-loup Gailly + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + * ALGORITHM + * + * The "deflation" process uses several Huffman trees. The more + * common source values are represented by shorter bit sequences. + * + * Each code tree is stored in a compressed form which is itself + * a Huffman encoding of the lengths of all the code strings (in + * ascending order by source values). The actual code strings are + * reconstructed from the lengths in the inflate process, as described + * in the deflate specification. + * + * REFERENCES + * + * Deutsch, L.P.,"'Deflate' Compressed Data Format Specification". + * Available in ftp.uu.net:/pub/archiving/zip/doc/deflate-1.1.doc + * + * Storer, James A. + * Data Compression: Methods and Theory, pp. 49-50. + * Computer Science Press, 1988. ISBN 0-7167-8156-5. + * + * Sedgewick, R. + * Algorithms, p290. + * Addison-Wesley, 1983. ISBN 0-201-06672-6. + */ + +/* @(#) $Id$ */ + +/* #define GEN_TREES_H */ + +#include "deflate.h" + +#ifdef DEBUG +# include <ctype.h> +#endif + +/* =========================================================================== + * Constants + */ + +#define MAX_BL_BITS 7 +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +#define END_BLOCK 256 +/* end of block literal code */ + +#define REP_3_6 16 +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +#define REPZ_3_10 17 +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +#define REPZ_11_138 18 +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +local const int extra_lbits[LENGTH_CODES] /* extra bits for each length code */ + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +local const int extra_dbits[D_CODES] /* extra bits for each distance code */ + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +local const int extra_blbits[BL_CODES]/* extra bits for each bit length code */ + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +local const uch bl_order[BL_CODES] + = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +#define Buf_size (8 * 2*sizeof(char)) +/* Number of bits used within bi_buf. (bi_buf might be implemented on + * more than 16 bits on some systems.) + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +#define DIST_CODE_LEN 512 /* see definition of array dist_code below */ + +#if defined(GEN_TREES_H) || !defined(STDC) +/* non ANSI compilers may not accept trees.h */ + +local ct_data static_ltree[L_CODES+2]; +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +local ct_data static_dtree[D_CODES]; +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +uch _dist_code[DIST_CODE_LEN]; +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +uch _length_code[MAX_MATCH-MIN_MATCH+1]; +/* length code for each normalized match length (0 == MIN_MATCH) */ + +local int base_length[LENGTH_CODES]; +/* First normalized length for each code (0 = MIN_MATCH) */ + +local int base_dist[D_CODES]; +/* First normalized distance for each code (0 = distance of 1) */ + +#else +# include "trees.h" +#endif /* GEN_TREES_H */ + +struct static_tree_desc_s { + const ct_data *static_tree; /* static tree or NULL */ + const intf *extra_bits; /* extra bits for each code or NULL */ + int extra_base; /* base index for extra_bits */ + int elems; /* max number of elements in the tree */ + int max_length; /* max bit length for the codes */ +}; + +local static_tree_desc static_l_desc = +{static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS}; + +local static_tree_desc static_d_desc = +{static_dtree, extra_dbits, 0, D_CODES, MAX_BITS}; + +local static_tree_desc static_bl_desc = +{(const ct_data *)0, extra_blbits, 0, BL_CODES, MAX_BL_BITS}; + +/* =========================================================================== + * Local (static) routines in this file. + */ + +local void tr_static_init OF((void)); +local void init_block OF((deflate_state *s)); +local void pqdownheap OF((deflate_state *s, ct_data *tree, int k)); +local void gen_bitlen OF((deflate_state *s, tree_desc *desc)); +local void gen_codes OF((ct_data *tree, int max_code, ushf *bl_count)); +local void build_tree OF((deflate_state *s, tree_desc *desc)); +local void scan_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local void send_tree OF((deflate_state *s, ct_data *tree, int max_code)); +local int build_bl_tree OF((deflate_state *s)); +local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, + int blcodes)); +local void compress_block OF((deflate_state *s, ct_data *ltree, + ct_data *dtree)); +local void set_data_type OF((deflate_state *s)); +local unsigned bi_reverse OF((unsigned value, int length)); +local void bi_windup OF((deflate_state *s)); +local void bi_flush OF((deflate_state *s)); +local void copy_block OF((deflate_state *s, charf *buf, unsigned len, + int header)); + +#ifdef GEN_TREES_H +local void gen_trees_header OF((void)); +#endif + +#ifndef DEBUG +# define send_code(s, c, tree) send_bits(s, tree[c].Code, tree[c].Len) + /* Send a code of the given tree. c and tree must not have side effects */ + +#else /* DEBUG */ +# define send_code(s, c, tree) \ + { if (z_verbose>2) fprintf(stderr,"\ncd %3d ",(c)); \ + send_bits(s, tree[c].Code, tree[c].Len); } +#endif + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +#define put_short(s, w) { \ + put_byte(s, (uch)((w) & 0xff)); \ + put_byte(s, (uch)((ush)(w) >> 8)); \ +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +#ifdef DEBUG +local void send_bits OF((deflate_state *s, int value, int length)); + +local void send_bits(s, value, length) + deflate_state *s; + int value; /* value to send */ + int length; /* number of bits */ +{ + Tracevv((stderr," l %2d v %4x ", length, value)); + Assert(length > 0 && length <= 15, "invalid length"); + s->bits_sent += (ulg)length; + + /* If not enough room in bi_buf, use (valid) bits from bi_buf and + * (16 - bi_valid) bits from value, leaving (width - (16-bi_valid)) + * unused bits in value. + */ + if (s->bi_valid > (int)Buf_size - length) { + s->bi_buf |= (value << s->bi_valid); + put_short(s, s->bi_buf); + s->bi_buf = (ush)value >> (Buf_size - s->bi_valid); + s->bi_valid += length - Buf_size; + } else { + s->bi_buf |= value << s->bi_valid; + s->bi_valid += length; + } +} +#else /* !DEBUG */ + +#define send_bits(s, value, length) \ +{ int len = length;\ + if (s->bi_valid > (int)Buf_size - len) {\ + int val = value;\ + s->bi_buf |= (val << s->bi_valid);\ + put_short(s, s->bi_buf);\ + s->bi_buf = (ush)val >> (Buf_size - s->bi_valid);\ + s->bi_valid += len - Buf_size;\ + } else {\ + s->bi_buf |= (value) << s->bi_valid;\ + s->bi_valid += len;\ + }\ +} +#endif /* DEBUG */ + + +/* the arguments must not have side effects */ + +/* =========================================================================== + * Initialize the various 'constant' tables. + */ +local void tr_static_init() +{ +#if defined(GEN_TREES_H) || !defined(STDC) + static int static_init_done = 0; + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + ush bl_count[MAX_BITS+1]; + /* number of codes at each bit length for an optimal tree */ + + if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + base_length[code] = length; + for (n = 0; n < (1<<extra_lbits[code]); n++) { + _length_code[length++] = (uch)code; + } + } + Assert (length == 256, "tr_static_init: length != 256"); + /* Note that the length 255 (match length 258) can be represented + * in two different ways: code 284 + 5 bits or code 285, so we + * overwrite length_code[255] to use the best encoding: + */ + _length_code[length-1] = (uch)code; + + /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1<<extra_dbits[code]); n++) { + _dist_code[dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: dist != 256"); + dist >>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + _dist_code[256 + dist++] = (uch)code; + } + } + Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) bl_count[bits] = 0; + n = 0; + while (n <= 143) static_ltree[n++].Len = 8, bl_count[8]++; + while (n <= 255) static_ltree[n++].Len = 9, bl_count[9]++; + while (n <= 279) static_ltree[n++].Len = 7, bl_count[7]++; + while (n <= 287) static_ltree[n++].Len = 8, bl_count[8]++; + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes((ct_data *)static_ltree, L_CODES+1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + static_dtree[n].Len = 5; + static_dtree[n].Code = bi_reverse((unsigned)n, 5); + } + static_init_done = 1; + +# ifdef GEN_TREES_H + gen_trees_header(); +# endif +#endif /* defined(GEN_TREES_H) || !defined(STDC) */ +} + +/* =========================================================================== + * Genererate the file trees.h describing the static trees. + */ +#ifdef GEN_TREES_H +# ifndef DEBUG +# include <stdio.h> +# endif + +# define SEPARATOR(i, last, width) \ + ((i) == (last)? "\n};\n\n" : \ + ((i) % (width) == (width)-1 ? ",\n" : ", ")) + +void gen_trees_header() +{ + FILE *header = fopen("trees.h", "w"); + int i; + + Assert (header != NULL, "Can't open trees.h"); + fprintf(header, + "/* header created automatically with -DGEN_TREES_H */\n\n"); + + fprintf(header, "local const ct_data static_ltree[L_CODES+2] = {\n"); + for (i = 0; i < L_CODES+2; i++) { + fprintf(header, "{{%3u},{%3u}}%s", static_ltree[i].Code, + static_ltree[i].Len, SEPARATOR(i, L_CODES+1, 5)); + } + + fprintf(header, "local const ct_data static_dtree[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "{{%2u},{%2u}}%s", static_dtree[i].Code, + static_dtree[i].Len, SEPARATOR(i, D_CODES-1, 5)); + } + + fprintf(header, "const uch _dist_code[DIST_CODE_LEN] = {\n"); + for (i = 0; i < DIST_CODE_LEN; i++) { + fprintf(header, "%2u%s", _dist_code[i], + SEPARATOR(i, DIST_CODE_LEN-1, 20)); + } + + fprintf(header, "const uch _length_code[MAX_MATCH-MIN_MATCH+1]= {\n"); + for (i = 0; i < MAX_MATCH-MIN_MATCH+1; i++) { + fprintf(header, "%2u%s", _length_code[i], + SEPARATOR(i, MAX_MATCH-MIN_MATCH, 20)); + } + + fprintf(header, "local const int base_length[LENGTH_CODES] = {\n"); + for (i = 0; i < LENGTH_CODES; i++) { + fprintf(header, "%1u%s", base_length[i], + SEPARATOR(i, LENGTH_CODES-1, 20)); + } + + fprintf(header, "local const int base_dist[D_CODES] = {\n"); + for (i = 0; i < D_CODES; i++) { + fprintf(header, "%5u%s", base_dist[i], + SEPARATOR(i, D_CODES-1, 10)); + } + + fclose(header); +} +#endif /* GEN_TREES_H */ + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +void _tr_init(s) + deflate_state *s; +{ + tr_static_init(); + + s->l_desc.dyn_tree = s->dyn_ltree; + s->l_desc.stat_desc = &static_l_desc; + + s->d_desc.dyn_tree = s->dyn_dtree; + s->d_desc.stat_desc = &static_d_desc; + + s->bl_desc.dyn_tree = s->bl_tree; + s->bl_desc.stat_desc = &static_bl_desc; + + s->bi_buf = 0; + s->bi_valid = 0; + s->last_eob_len = 8; /* enough lookahead for inflate */ +#ifdef DEBUG + s->compressed_len = 0L; + s->bits_sent = 0L; +#endif + + /* Initialize the first block of the first file: */ + init_block(s); +} + +/* =========================================================================== + * Initialize a new block. + */ +local void init_block(s) + deflate_state *s; +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) s->dyn_ltree[n].Freq = 0; + for (n = 0; n < D_CODES; n++) s->dyn_dtree[n].Freq = 0; + for (n = 0; n < BL_CODES; n++) s->bl_tree[n].Freq = 0; + + s->dyn_ltree[END_BLOCK].Freq = 1; + s->opt_len = s->static_len = 0L; + s->last_lit = s->matches = 0; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(s, tree, top) \ +{\ + top = s->heap[SMALLEST]; \ + s->heap[SMALLEST] = s->heap[s->heap_len--]; \ + pqdownheap(s, tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m, depth) \ + (tree[n].Freq < tree[m].Freq || \ + (tree[n].Freq == tree[m].Freq && depth[n] <= depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +local void pqdownheap(s, tree, k) + deflate_state *s; + ct_data *tree; /* the tree to restore */ + int k; /* node to move down */ +{ + int v = s->heap[k]; + int j = k << 1; /* left son of k */ + while (j <= s->heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < s->heap_len && + smaller(tree, s->heap[j+1], s->heap[j], s->depth)) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s->heap[j], s->depth)) break; + + /* Exchange v with the smallest son */ + s->heap[k] = s->heap[j]; k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s->heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +local void gen_bitlen(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + int max_code = desc->max_code; + const ct_data *stree = desc->stat_desc->static_tree; + const intf *extra = desc->stat_desc->extra_bits; + int base = desc->stat_desc->extra_base; + int max_length = desc->stat_desc->max_length; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) s->bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s->heap[s->heap_max]].Len = 0; /* root of the heap */ + + for (h = s->heap_max+1; h < HEAP_SIZE; h++) { + n = s->heap[h]; + bits = tree[tree[n].Dad].Len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].Len = (ush)bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s->bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].Freq; + s->opt_len += (ulg)f * (bits + xbits); + if (stree) s->static_len += (ulg)f * (stree[n].Len + xbits); + } + if (overflow == 0) return; + + Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (s->bl_count[bits] == 0) bits--; + s->bl_count[bits]--; /* move one leaf down the tree */ + s->bl_count[bits+1] += 2; /* move one overflow item as its brother */ + s->bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = s->bl_count[bits]; + while (n != 0) { + m = s->heap[--h]; + if (m > max_code) continue; + if ((unsigned) tree[m].Len != (unsigned) bits) { + Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s->opt_len += ((long)bits - (long)tree[m].Len) + *(long)tree[m].Freq; + tree[m].Len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +local void gen_codes (tree, max_code, bl_count) + ct_data *tree; /* the tree to decorate */ + int max_code; /* largest code with non zero frequency */ + ushf *bl_count; /* number of codes at each bit length */ +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (code + bl_count[bits-1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1, + "inconsistent bit counts"); + Tracev((stderr,"\ngen_codes: max_code %d ", max_code)); + + for (n = 0; n <= max_code; n++) { + int len = tree[n].Len; + if (len == 0) continue; + /* Now reverse the bits */ + tree[n].Code = bi_reverse(next_code[len]++, len); + + Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ", + n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1)); + } +} + +/* =========================================================================== + * Construct one Huffman tree and assigns the code bit strings and lengths. + * Update the total bit length for the current block. + * IN assertion: the field freq is set for all tree elements. + * OUT assertions: the fields len and code are set to the optimal bit length + * and corresponding code. The length opt_len is updated; static_len is + * also updated if stree is not null. The field max_code is set. + */ +local void build_tree(s, desc) + deflate_state *s; + tree_desc *desc; /* the tree descriptor */ +{ + ct_data *tree = desc->dyn_tree; + const ct_data *stree = desc->stat_desc->static_tree; + int elems = desc->stat_desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s->heap_len = 0, s->heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].Freq != 0) { + s->heap[++(s->heap_len)] = max_code = n; + s->depth[n] = 0; + } else { + tree[n].Len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s->heap_len < 2) { + node = s->heap[++(s->heap_len)] = (max_code < 2 ? ++max_code : 0); + tree[node].Freq = 1; + s->depth[node] = 0; + s->opt_len--; if (stree) s->static_len -= stree[node].Len; + /* node is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s->heap_len/2; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + pqremove(s, tree, n); /* n = node of least frequency */ + m = s->heap[SMALLEST]; /* m = node of next least frequency */ + + s->heap[--(s->heap_max)] = n; /* keep the nodes sorted by frequency */ + s->heap[--(s->heap_max)] = m; + + /* Create a new node father of n and m */ + tree[node].Freq = tree[n].Freq + tree[m].Freq; + s->depth[node] = (uch)((s->depth[n] >= s->depth[m] ? + s->depth[n] : s->depth[m]) + 1); + tree[n].Dad = tree[m].Dad = (ush)node; +#ifdef DUMP_BL_TREE + if (tree == s->bl_tree) { + fprintf(stderr,"\nnode %d(%d), sons %d(%d) %d(%d)", + node, tree[node].Freq, n, tree[n].Freq, m, tree[m].Freq); + } +#endif + /* and insert the new node in the heap */ + s->heap[SMALLEST] = node++; + pqdownheap(s, tree, SMALLEST); + + } while (s->heap_len >= 2); + + s->heap[--(s->heap_max)] = s->heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, (tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes ((ct_data *)tree, max_code, s->bl_count); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +local void scan_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].Len = (ush)0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + s->bl_tree[curlen].Freq += count; + } else if (curlen != 0) { + if (curlen != prevlen) s->bl_tree[curlen].Freq++; + s->bl_tree[REP_3_6].Freq++; + } else if (count <= 10) { + s->bl_tree[REPZ_3_10].Freq++; + } else { + s->bl_tree[REPZ_11_138].Freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +local void send_tree (s, tree, max_code) + deflate_state *s; + ct_data *tree; /* the tree to be scanned */ + int max_code; /* and its largest code of non zero frequency */ +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].Len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].Len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(s, curlen, s->bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(s, curlen, s->bl_tree); count--; + } + Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s->bl_tree); send_bits(s, count-3, 2); + + } else if (count <= 10) { + send_code(s, REPZ_3_10, s->bl_tree); send_bits(s, count-3, 3); + + } else { + send_code(s, REPZ_11_138, s->bl_tree); send_bits(s, count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +local int build_bl_tree(s) + deflate_state *s; +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, (ct_data *)s->dyn_ltree, s->l_desc.max_code); + scan_tree(s, (ct_data *)s->dyn_dtree, s->d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, (tree_desc *)(&(s->bl_desc))); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (s->bl_tree[bl_order[max_blindex]].Len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + s->opt_len += 3*(max_blindex+1) + 5+5+4; + Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + s->opt_len, s->static_len)); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +local void send_all_trees(s, lcodes, dcodes, blcodes) + deflate_state *s; + int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + int rank; /* index in bl_order */ + + Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes-257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes-1, 5); + send_bits(s, blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s->bl_tree[bl_order[rank]].Len, 3); + } + Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_ltree, lcodes-1); /* literal tree */ + Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, (ct_data *)s->dyn_dtree, dcodes-1); /* distance tree */ + Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +} + +/* =========================================================================== + * Send a stored block + */ +void _tr_stored_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK<<1)+eof, 3); /* send block type */ +#ifdef DEBUG + s->compressed_len = (s->compressed_len + 3 + 7) & (ulg)~7L; + s->compressed_len += (stored_len + 4) << 3; +#endif + copy_block(s, buf, (unsigned)stored_len, 1); /* with header */ +} + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + * The current inflate code requires 9 bits of lookahead. If the + * last two codes for the previous block (real code plus EOB) were coded + * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode + * the last real code. In this case we send two empty static blocks instead + * of one. (There are no problems if the previous block is stored or fixed.) + * To simplify the code, we assume the worst case of last real code encoded + * on one bit only. + */ +void _tr_align(s) + deflate_state *s; +{ + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ +#endif + bi_flush(s); + /* Of the 10 bits for the empty block, we have already sent + * (10 - bi_valid) bits. The lookahead for the last real code (before + * the EOB of the previous block) was thus at least one plus the length + * of the EOB plus what we have just sent of the empty static block. + */ + if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { + send_bits(s, STATIC_TREES<<1, 3); + send_code(s, END_BLOCK, static_ltree); +#ifdef DEBUG + s->compressed_len += 10L; +#endif + bi_flush(s); + } + s->last_eob_len = 7; +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +void _tr_flush_block(s, buf, stored_len, eof) + deflate_state *s; + charf *buf; /* input block, or NULL if too old */ + ulg stored_len; /* length of input block */ + int eof; /* true if this is the last block for a file */ +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s->level > 0) { + + /* Check if the file is binary or text */ + if (stored_len > 0 && s->strm->data_type == Z_UNKNOWN) + set_data_type(s); + + /* Construct the literal and distance trees */ + build_tree(s, (tree_desc *)(&(s->l_desc))); + Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + + build_tree(s, (tree_desc *)(&(s->d_desc))); + Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s->opt_len+3+7)>>3; + static_lenb = (s->static_len+3+7)>>3; + + Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + } else { + Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + +#ifdef FORCE_STORED + if (buf != (char*)0) { /* force stored block */ +#else + if (stored_len+4 <= opt_lenb && buf != (char*)0) { + /* 4: two words for the lengths */ +#endif + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block(s, buf, stored_len, eof); + +#ifdef FORCE_STATIC + } else if (static_lenb >= 0) { /* force static trees */ +#else + } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { +#endif + send_bits(s, (STATIC_TREES<<1)+eof, 3); + compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->static_len; +#endif + } else { + send_bits(s, (DYN_TREES<<1)+eof, 3); + send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, + max_blindex+1); + compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); +#ifdef DEBUG + s->compressed_len += 3 + s->opt_len; +#endif + } + Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (eof) { + bi_windup(s); +#ifdef DEBUG + s->compressed_len += 7; /* align on byte boundary */ +#endif + } + Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + s->compressed_len-7*eof)); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int _tr_tally (s, dist, lc) + deflate_state *s; + unsigned dist; /* distance of matched string */ + unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + s->d_buf[s->last_lit] = (ush)dist; + s->l_buf[s->last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + s->dyn_ltree[lc].Freq++; + } else { + s->matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert((ush)dist < (ush)MAX_DIST(s) && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s->dyn_ltree[_length_code[lc]+LITERALS+1].Freq++; + s->dyn_dtree[d_code(dist)].Freq++; + } + +#ifdef TRUNCATE_BLOCK + /* Try to guess if it is profitable to stop the current block here */ + if ((s->last_lit & 0x1fff) == 0 && s->level > 2) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)s->last_lit*8L; + ulg in_length = (ulg)((long)s->strstart - s->block_start); + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)s->dyn_dtree[dcode].Freq * + (5L+extra_dbits[dcode]); + } + out_length >>= 3; + Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + s->last_lit, in_length, out_length, + 100L - out_length*100L/in_length)); + if (s->matches < s->last_lit/2 && out_length < in_length/2) return 1; + } +#endif + return (s->last_lit == s->lit_bufsize-1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +local void compress_block(s, ltree, dtree) + deflate_state *s; + ct_data *ltree; /* literal tree */ + ct_data *dtree; /* distance tree */ +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (s->last_lit != 0) do { + dist = s->d_buf[lx]; + lc = s->l_buf[lx++]; + if (dist == 0) { + send_code(s, lc, ltree); /* send a literal byte */ + Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + "pendingBuf overflow"); + + } while (lx < s->last_lit); + + send_code(s, END_BLOCK, ltree); + s->last_eob_len = ltree[END_BLOCK].Len; +} + +/* =========================================================================== + * Set the data type to BINARY or TEXT, using a crude approximation: + * set it to Z_TEXT if all symbols are either printable characters (33 to 255) + * or white spaces (9 to 13, or 32); or set it to Z_BINARY otherwise. + * IN assertion: the fields Freq of dyn_ltree are set. + */ +local void set_data_type(s) + deflate_state *s; +{ + int n; + + for (n = 0; n < 9; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + if (n == 9) + for (n = 14; n < 32; n++) + if (s->dyn_ltree[n].Freq != 0) + break; + s->strm->data_type = (n == 32) ? Z_TEXT : Z_BINARY; +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +local unsigned bi_reverse(code, len) + unsigned code; /* the value to invert */ + int len; /* its bit length */ +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +local void bi_flush(s) + deflate_state *s; +{ + if (s->bi_valid == 16) { + put_short(s, s->bi_buf); + s->bi_buf = 0; + s->bi_valid = 0; + } else if (s->bi_valid >= 8) { + put_byte(s, (Byte)s->bi_buf); + s->bi_buf >>= 8; + s->bi_valid -= 8; + } +} + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +local void bi_windup(s) + deflate_state *s; +{ + if (s->bi_valid > 8) { + put_short(s, s->bi_buf); + } else if (s->bi_valid > 0) { + put_byte(s, (Byte)s->bi_buf); + } + s->bi_buf = 0; + s->bi_valid = 0; +#ifdef DEBUG + s->bits_sent = (s->bits_sent+7) & ~7; +#endif +} + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +local void copy_block(s, buf, len, header) + deflate_state *s; + charf *buf; /* the input data */ + unsigned len; /* its length */ + int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + s->last_eob_len = 8; /* enough lookahead for inflate */ + + if (header) { + put_short(s, (ush)len); + put_short(s, (ush)~len); +#ifdef DEBUG + s->bits_sent += 2*16; +#endif + } +#ifdef DEBUG + s->bits_sent += (ulg)len<<3; +#endif + while (len--) { + put_byte(s, *buf++); + } +} diff --git a/src/lib/zlib/trees.h b/src/lib/zlib/trees.h new file mode 100644 index 00000000000..72facf900f7 --- /dev/null +++ b/src/lib/zlib/trees.h @@ -0,0 +1,128 @@ +/* header created automatically with -DGEN_TREES_H */ + +local const ct_data static_ltree[L_CODES+2] = { +{{ 12},{ 8}}, {{140},{ 8}}, {{ 76},{ 8}}, {{204},{ 8}}, {{ 44},{ 8}}, +{{172},{ 8}}, {{108},{ 8}}, {{236},{ 8}}, {{ 28},{ 8}}, {{156},{ 8}}, +{{ 92},{ 8}}, {{220},{ 8}}, {{ 60},{ 8}}, {{188},{ 8}}, {{124},{ 8}}, +{{252},{ 8}}, {{ 2},{ 8}}, {{130},{ 8}}, {{ 66},{ 8}}, {{194},{ 8}}, +{{ 34},{ 8}}, {{162},{ 8}}, {{ 98},{ 8}}, {{226},{ 8}}, {{ 18},{ 8}}, +{{146},{ 8}}, {{ 82},{ 8}}, {{210},{ 8}}, {{ 50},{ 8}}, {{178},{ 8}}, +{{114},{ 8}}, {{242},{ 8}}, {{ 10},{ 8}}, {{138},{ 8}}, {{ 74},{ 8}}, +{{202},{ 8}}, {{ 42},{ 8}}, {{170},{ 8}}, {{106},{ 8}}, {{234},{ 8}}, +{{ 26},{ 8}}, {{154},{ 8}}, {{ 90},{ 8}}, {{218},{ 8}}, {{ 58},{ 8}}, +{{186},{ 8}}, {{122},{ 8}}, {{250},{ 8}}, {{ 6},{ 8}}, {{134},{ 8}}, +{{ 70},{ 8}}, {{198},{ 8}}, {{ 38},{ 8}}, {{166},{ 8}}, {{102},{ 8}}, +{{230},{ 8}}, {{ 22},{ 8}}, {{150},{ 8}}, {{ 86},{ 8}}, {{214},{ 8}}, +{{ 54},{ 8}}, {{182},{ 8}}, {{118},{ 8}}, {{246},{ 8}}, {{ 14},{ 8}}, +{{142},{ 8}}, {{ 78},{ 8}}, {{206},{ 8}}, {{ 46},{ 8}}, {{174},{ 8}}, +{{110},{ 8}}, {{238},{ 8}}, {{ 30},{ 8}}, {{158},{ 8}}, {{ 94},{ 8}}, +{{222},{ 8}}, {{ 62},{ 8}}, {{190},{ 8}}, {{126},{ 8}}, {{254},{ 8}}, +{{ 1},{ 8}}, {{129},{ 8}}, {{ 65},{ 8}}, {{193},{ 8}}, {{ 33},{ 8}}, +{{161},{ 8}}, {{ 97},{ 8}}, {{225},{ 8}}, {{ 17},{ 8}}, {{145},{ 8}}, +{{ 81},{ 8}}, {{209},{ 8}}, {{ 49},{ 8}}, {{177},{ 8}}, {{113},{ 8}}, +{{241},{ 8}}, {{ 9},{ 8}}, {{137},{ 8}}, {{ 73},{ 8}}, {{201},{ 8}}, +{{ 41},{ 8}}, {{169},{ 8}}, {{105},{ 8}}, {{233},{ 8}}, {{ 25},{ 8}}, +{{153},{ 8}}, {{ 89},{ 8}}, {{217},{ 8}}, {{ 57},{ 8}}, {{185},{ 8}}, +{{121},{ 8}}, {{249},{ 8}}, {{ 5},{ 8}}, {{133},{ 8}}, {{ 69},{ 8}}, +{{197},{ 8}}, {{ 37},{ 8}}, {{165},{ 8}}, {{101},{ 8}}, {{229},{ 8}}, +{{ 21},{ 8}}, {{149},{ 8}}, {{ 85},{ 8}}, {{213},{ 8}}, {{ 53},{ 8}}, +{{181},{ 8}}, {{117},{ 8}}, {{245},{ 8}}, {{ 13},{ 8}}, {{141},{ 8}}, +{{ 77},{ 8}}, {{205},{ 8}}, {{ 45},{ 8}}, {{173},{ 8}}, {{109},{ 8}}, +{{237},{ 8}}, {{ 29},{ 8}}, {{157},{ 8}}, {{ 93},{ 8}}, {{221},{ 8}}, +{{ 61},{ 8}}, {{189},{ 8}}, {{125},{ 8}}, {{253},{ 8}}, {{ 19},{ 9}}, +{{275},{ 9}}, {{147},{ 9}}, {{403},{ 9}}, {{ 83},{ 9}}, {{339},{ 9}}, +{{211},{ 9}}, {{467},{ 9}}, {{ 51},{ 9}}, {{307},{ 9}}, {{179},{ 9}}, +{{435},{ 9}}, {{115},{ 9}}, {{371},{ 9}}, {{243},{ 9}}, {{499},{ 9}}, +{{ 11},{ 9}}, {{267},{ 9}}, {{139},{ 9}}, {{395},{ 9}}, {{ 75},{ 9}}, +{{331},{ 9}}, {{203},{ 9}}, {{459},{ 9}}, {{ 43},{ 9}}, {{299},{ 9}}, +{{171},{ 9}}, {{427},{ 9}}, {{107},{ 9}}, {{363},{ 9}}, {{235},{ 9}}, +{{491},{ 9}}, {{ 27},{ 9}}, {{283},{ 9}}, {{155},{ 9}}, {{411},{ 9}}, +{{ 91},{ 9}}, {{347},{ 9}}, {{219},{ 9}}, {{475},{ 9}}, {{ 59},{ 9}}, +{{315},{ 9}}, {{187},{ 9}}, {{443},{ 9}}, {{123},{ 9}}, {{379},{ 9}}, +{{251},{ 9}}, {{507},{ 9}}, {{ 7},{ 9}}, {{263},{ 9}}, {{135},{ 9}}, +{{391},{ 9}}, {{ 71},{ 9}}, {{327},{ 9}}, {{199},{ 9}}, {{455},{ 9}}, +{{ 39},{ 9}}, {{295},{ 9}}, {{167},{ 9}}, {{423},{ 9}}, {{103},{ 9}}, +{{359},{ 9}}, {{231},{ 9}}, {{487},{ 9}}, {{ 23},{ 9}}, {{279},{ 9}}, +{{151},{ 9}}, {{407},{ 9}}, {{ 87},{ 9}}, {{343},{ 9}}, {{215},{ 9}}, +{{471},{ 9}}, {{ 55},{ 9}}, {{311},{ 9}}, {{183},{ 9}}, {{439},{ 9}}, +{{119},{ 9}}, {{375},{ 9}}, {{247},{ 9}}, {{503},{ 9}}, {{ 15},{ 9}}, +{{271},{ 9}}, {{143},{ 9}}, {{399},{ 9}}, {{ 79},{ 9}}, {{335},{ 9}}, +{{207},{ 9}}, {{463},{ 9}}, {{ 47},{ 9}}, {{303},{ 9}}, {{175},{ 9}}, +{{431},{ 9}}, {{111},{ 9}}, {{367},{ 9}}, {{239},{ 9}}, {{495},{ 9}}, +{{ 31},{ 9}}, {{287},{ 9}}, {{159},{ 9}}, {{415},{ 9}}, {{ 95},{ 9}}, +{{351},{ 9}}, {{223},{ 9}}, {{479},{ 9}}, {{ 63},{ 9}}, {{319},{ 9}}, +{{191},{ 9}}, {{447},{ 9}}, {{127},{ 9}}, {{383},{ 9}}, {{255},{ 9}}, +{{511},{ 9}}, {{ 0},{ 7}}, {{ 64},{ 7}}, {{ 32},{ 7}}, {{ 96},{ 7}}, +{{ 16},{ 7}}, {{ 80},{ 7}}, {{ 48},{ 7}}, {{112},{ 7}}, {{ 8},{ 7}}, +{{ 72},{ 7}}, {{ 40},{ 7}}, {{104},{ 7}}, {{ 24},{ 7}}, {{ 88},{ 7}}, +{{ 56},{ 7}}, {{120},{ 7}}, {{ 4},{ 7}}, {{ 68},{ 7}}, {{ 36},{ 7}}, +{{100},{ 7}}, {{ 20},{ 7}}, {{ 84},{ 7}}, {{ 52},{ 7}}, {{116},{ 7}}, +{{ 3},{ 8}}, {{131},{ 8}}, {{ 67},{ 8}}, {{195},{ 8}}, {{ 35},{ 8}}, +{{163},{ 8}}, {{ 99},{ 8}}, {{227},{ 8}} +}; + +local const ct_data static_dtree[D_CODES] = { +{{ 0},{ 5}}, {{16},{ 5}}, {{ 8},{ 5}}, {{24},{ 5}}, {{ 4},{ 5}}, +{{20},{ 5}}, {{12},{ 5}}, {{28},{ 5}}, {{ 2},{ 5}}, {{18},{ 5}}, +{{10},{ 5}}, {{26},{ 5}}, {{ 6},{ 5}}, {{22},{ 5}}, {{14},{ 5}}, +{{30},{ 5}}, {{ 1},{ 5}}, {{17},{ 5}}, {{ 9},{ 5}}, {{25},{ 5}}, +{{ 5},{ 5}}, {{21},{ 5}}, {{13},{ 5}}, {{29},{ 5}}, {{ 3},{ 5}}, +{{19},{ 5}}, {{11},{ 5}}, {{27},{ 5}}, {{ 7},{ 5}}, {{23},{ 5}} +}; + +const uch _dist_code[DIST_CODE_LEN] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, + 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, +10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, +11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, +12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, +13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, +14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, +15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 16, 17, +18, 18, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, +28, 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, +29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29 +}; + +const uch _length_code[MAX_MATCH-MIN_MATCH+1]= { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, +13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, +17, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, +19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, +22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, +23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, +26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, +27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 +}; + +local const int base_length[LENGTH_CODES] = { +0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 32, 40, 48, 56, +64, 80, 96, 112, 128, 160, 192, 224, 0 +}; + +local const int base_dist[D_CODES] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, + 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, + 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576 +}; + diff --git a/src/lib/zlib/uncompr.c b/src/lib/zlib/uncompr.c new file mode 100644 index 00000000000..b59e3d0defb --- /dev/null +++ b/src/lib/zlib/uncompr.c @@ -0,0 +1,61 @@ +/* uncompr.c -- decompress a memory buffer + * Copyright (C) 1995-2003 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#define ZLIB_INTERNAL +#include "zlib.h" + +/* =========================================================================== + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted. +*/ +int ZEXPORT uncompress (dest, destLen, source, sourceLen) + Bytef *dest; + uLongf *destLen; + const Bytef *source; + uLong sourceLen; +{ + z_stream stream; + int err; + + stream.next_in = (Bytef*)source; + stream.avail_in = (uInt)sourceLen; + /* Check for source > 64K on 16-bit machine: */ + if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; + + stream.next_out = dest; + stream.avail_out = (uInt)*destLen; + if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; + + stream.zalloc = (alloc_func)0; + stream.zfree = (free_func)0; + + err = inflateInit(&stream); + if (err != Z_OK) return err; + + err = inflate(&stream, Z_FINISH); + if (err != Z_STREAM_END) { + inflateEnd(&stream); + if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) + return Z_DATA_ERROR; + return err; + } + *destLen = stream.total_out; + + err = inflateEnd(&stream); + return err; +} diff --git a/src/lib/zlib/zconf.h b/src/lib/zlib/zconf.h new file mode 100644 index 00000000000..03a9431c8be --- /dev/null +++ b/src/lib/zlib/zconf.h @@ -0,0 +1,332 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflateParams z_deflateParams +# define deflateBound z_deflateBound +# define deflatePrime z_deflatePrime +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateCopy z_inflateCopy +# define inflateReset z_inflateReset +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table +# define zError z_zError + +# define alloc_func z_alloc_func +# define free_func z_free_func +# define in_func z_in_func +# define out_func z_out_func +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include <windows.h> + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ +# include <sys/types.h> /* for off_t */ +# include <unistd.h> /* for SEEK_* and off_t */ +# ifdef VMS +# include <unixio.h> /* for off_t */ +# endif +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +# ifdef FAR +# undef FAR +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(deflateBound,"DEBND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(compressBound,"CMBND") +# pragma map(inflate_table,"INTABL") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/src/lib/zlib/zconf.in.h b/src/lib/zlib/zconf.in.h new file mode 100644 index 00000000000..03a9431c8be --- /dev/null +++ b/src/lib/zlib/zconf.in.h @@ -0,0 +1,332 @@ +/* zconf.h -- configuration of the zlib compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#ifndef ZCONF_H +#define ZCONF_H + +/* + * If you *really* need a unique prefix for all types and library functions, + * compile with -DZ_PREFIX. The "standard" zlib should be compiled without it. + */ +#ifdef Z_PREFIX +# define deflateInit_ z_deflateInit_ +# define deflate z_deflate +# define deflateEnd z_deflateEnd +# define inflateInit_ z_inflateInit_ +# define inflate z_inflate +# define inflateEnd z_inflateEnd +# define deflateInit2_ z_deflateInit2_ +# define deflateSetDictionary z_deflateSetDictionary +# define deflateCopy z_deflateCopy +# define deflateReset z_deflateReset +# define deflateParams z_deflateParams +# define deflateBound z_deflateBound +# define deflatePrime z_deflatePrime +# define inflateInit2_ z_inflateInit2_ +# define inflateSetDictionary z_inflateSetDictionary +# define inflateSync z_inflateSync +# define inflateSyncPoint z_inflateSyncPoint +# define inflateCopy z_inflateCopy +# define inflateReset z_inflateReset +# define inflateBack z_inflateBack +# define inflateBackEnd z_inflateBackEnd +# define compress z_compress +# define compress2 z_compress2 +# define compressBound z_compressBound +# define uncompress z_uncompress +# define adler32 z_adler32 +# define crc32 z_crc32 +# define get_crc_table z_get_crc_table +# define zError z_zError + +# define alloc_func z_alloc_func +# define free_func z_free_func +# define in_func z_in_func +# define out_func z_out_func +# define Byte z_Byte +# define uInt z_uInt +# define uLong z_uLong +# define Bytef z_Bytef +# define charf z_charf +# define intf z_intf +# define uIntf z_uIntf +# define uLongf z_uLongf +# define voidpf z_voidpf +# define voidp z_voidp +#endif + +#if defined(__MSDOS__) && !defined(MSDOS) +# define MSDOS +#endif +#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2) +# define OS2 +#endif +#if defined(_WINDOWS) && !defined(WINDOWS) +# define WINDOWS +#endif +#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__) +# ifndef WIN32 +# define WIN32 +# endif +#endif +#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32) +# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__) +# ifndef SYS16BIT +# define SYS16BIT +# endif +# endif +#endif + +/* + * Compile with -DMAXSEG_64K if the alloc function cannot allocate more + * than 64k bytes at a time (needed on systems with 16-bit int). + */ +#ifdef SYS16BIT +# define MAXSEG_64K +#endif +#ifdef MSDOS +# define UNALIGNED_OK +#endif + +#ifdef __STDC_VERSION__ +# ifndef STDC +# define STDC +# endif +# if __STDC_VERSION__ >= 199901L +# ifndef STDC99 +# define STDC99 +# endif +# endif +#endif +#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus)) +# define STDC +#endif +#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__)) +# define STDC +#endif +#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32)) +# define STDC +#endif +#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__)) +# define STDC +#endif + +#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */ +# define STDC +#endif + +#ifndef STDC +# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */ +# define const /* note: need a more gentle solution here */ +# endif +#endif + +/* Some Mac compilers merge all .h files incorrectly: */ +#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__) +# define NO_DUMMY_DECL +#endif + +/* Maximum value for memLevel in deflateInit2 */ +#ifndef MAX_MEM_LEVEL +# ifdef MAXSEG_64K +# define MAX_MEM_LEVEL 8 +# else +# define MAX_MEM_LEVEL 9 +# endif +#endif + +/* Maximum value for windowBits in deflateInit2 and inflateInit2. + * WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files + * created by gzip. (Files created by minigzip can still be extracted by + * gzip.) + */ +#ifndef MAX_WBITS +# define MAX_WBITS 15 /* 32K LZ77 window */ +#endif + +/* The memory requirements for deflate are (in bytes): + (1 << (windowBits+2)) + (1 << (memLevel+9)) + that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values) + plus a few kilobytes for small objects. For example, if you want to reduce + the default memory requirements from 256K to 128K, compile with + make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7" + Of course this will generally degrade compression (there's no free lunch). + + The memory requirements for inflate are (in bytes) 1 << windowBits + that is, 32K for windowBits=15 (default value) plus a few kilobytes + for small objects. +*/ + + /* Type declarations */ + +#ifndef OF /* function prototypes */ +# ifdef STDC +# define OF(args) args +# else +# define OF(args) () +# endif +#endif + +/* The following definitions for FAR are needed only for MSDOS mixed + * model programming (small or medium model with some far allocations). + * This was tested only with MSC; for other MSDOS compilers you may have + * to define NO_MEMCPY in zutil.h. If you don't need the mixed model, + * just define FAR to be empty. + */ +#ifdef SYS16BIT +# if defined(M_I86SM) || defined(M_I86MM) + /* MSC small or medium model */ +# define SMALL_MEDIUM +# ifdef _MSC_VER +# define FAR _far +# else +# define FAR far +# endif +# endif +# if (defined(__SMALL__) || defined(__MEDIUM__)) + /* Turbo C small or medium model */ +# define SMALL_MEDIUM +# ifdef __BORLANDC__ +# define FAR _far +# else +# define FAR far +# endif +# endif +#endif + +#if defined(WINDOWS) || defined(WIN32) + /* If building or using zlib as a DLL, define ZLIB_DLL. + * This is not mandatory, but it offers a little performance increase. + */ +# ifdef ZLIB_DLL +# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500)) +# ifdef ZLIB_INTERNAL +# define ZEXTERN extern __declspec(dllexport) +# else +# define ZEXTERN extern __declspec(dllimport) +# endif +# endif +# endif /* ZLIB_DLL */ + /* If building or using zlib with the WINAPI/WINAPIV calling convention, + * define ZLIB_WINAPI. + * Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI. + */ +# ifdef ZLIB_WINAPI +# ifdef FAR +# undef FAR +# endif +# include <windows.h> + /* No need for _export, use ZLIB.DEF instead. */ + /* For complete Windows compatibility, use WINAPI, not __stdcall. */ +# define ZEXPORT WINAPI +# ifdef WIN32 +# define ZEXPORTVA WINAPIV +# else +# define ZEXPORTVA FAR CDECL +# endif +# endif +#endif + +#if defined (__BEOS__) +# ifdef ZLIB_DLL +# ifdef ZLIB_INTERNAL +# define ZEXPORT __declspec(dllexport) +# define ZEXPORTVA __declspec(dllexport) +# else +# define ZEXPORT __declspec(dllimport) +# define ZEXPORTVA __declspec(dllimport) +# endif +# endif +#endif + +#ifndef ZEXTERN +# define ZEXTERN extern +#endif +#ifndef ZEXPORT +# define ZEXPORT +#endif +#ifndef ZEXPORTVA +# define ZEXPORTVA +#endif + +#ifndef FAR +# define FAR +#endif + +#if !defined(__MACTYPES__) +typedef unsigned char Byte; /* 8 bits */ +#endif +typedef unsigned int uInt; /* 16 bits or more */ +typedef unsigned long uLong; /* 32 bits or more */ + +#ifdef SMALL_MEDIUM + /* Borland C/C++ and some old MSC versions ignore FAR inside typedef */ +# define Bytef Byte FAR +#else + typedef Byte FAR Bytef; +#endif +typedef char FAR charf; +typedef int FAR intf; +typedef uInt FAR uIntf; +typedef uLong FAR uLongf; + +#ifdef STDC + typedef void const *voidpc; + typedef void FAR *voidpf; + typedef void *voidp; +#else + typedef Byte const *voidpc; + typedef Byte FAR *voidpf; + typedef Byte *voidp; +#endif + +#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */ +# include <sys/types.h> /* for off_t */ +# include <unistd.h> /* for SEEK_* and off_t */ +# ifdef VMS +# include <unixio.h> /* for off_t */ +# endif +# define z_off_t off_t +#endif +#ifndef SEEK_SET +# define SEEK_SET 0 /* Seek from beginning of file. */ +# define SEEK_CUR 1 /* Seek from current position. */ +# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */ +#endif +#ifndef z_off_t +# define z_off_t long +#endif + +#if defined(__OS400__) +# define NO_vsnprintf +#endif + +#if defined(__MVS__) +# define NO_vsnprintf +# ifdef FAR +# undef FAR +# endif +#endif + +/* MVS linker does not support external names larger than 8 bytes */ +#if defined(__MVS__) +# pragma map(deflateInit_,"DEIN") +# pragma map(deflateInit2_,"DEIN2") +# pragma map(deflateEnd,"DEEND") +# pragma map(deflateBound,"DEBND") +# pragma map(inflateInit_,"ININ") +# pragma map(inflateInit2_,"ININ2") +# pragma map(inflateEnd,"INEND") +# pragma map(inflateSync,"INSY") +# pragma map(inflateSetDictionary,"INSEDI") +# pragma map(compressBound,"CMBND") +# pragma map(inflate_table,"INTABL") +# pragma map(inflate_fast,"INFA") +# pragma map(inflate_copyright,"INCOPY") +#endif + +#endif /* ZCONF_H */ diff --git a/src/lib/zlib/zlib.h b/src/lib/zlib/zlib.h new file mode 100644 index 00000000000..022817927ce --- /dev/null +++ b/src/lib/zlib/zlib.h @@ -0,0 +1,1357 @@ +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.3, July 18th, 2005 + + Copyright (C) 1995-2005 Jean-loup Gailly and Mark Adler + + 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. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). +*/ + +#ifndef ZLIB_H +#define ZLIB_H + +#include "zconf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define ZLIB_VERSION "1.2.3" +#define ZLIB_VERNUM 0x1230 + +/* + The 'zlib' compression library provides in-memory compression and + decompression functions, including integrity checks of the uncompressed + data. This version of the library supports only one compression method + (deflation) but other algorithms will be added later and will have the same + stream interface. + + Compression can be done in a single step if the buffers are large + enough (for example if an input file is mmap'ed), or can be done by + repeated calls of the compression function. In the latter case, the + application must provide more input and/or consume the output + (providing more output space) before each call. + + The compressed data format used by default by the in-memory functions is + the zlib format, which is a zlib wrapper documented in RFC 1950, wrapped + around a deflate stream, which is itself documented in RFC 1951. + + The library also supports reading and writing files in gzip (.gz) format + with an interface similar to that of stdio using the functions that start + with "gz". The gzip format is different from the zlib format. gzip is a + gzip wrapper, documented in RFC 1952, wrapped around a deflate stream. + + This library can optionally read and write gzip streams in memory as well. + + The zlib format was designed to be compact and fast for use in memory + and on communications channels. The gzip format was designed for single- + file compression on file systems, has a larger header than zlib to maintain + directory information, and uses a different, slower check method than zlib. + + The library does not install any signal handler. The decoder checks + the consistency of the compressed data, so the library should never + crash even in case of corrupted input. +*/ + +typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size)); +typedef void (*free_func) OF((voidpf opaque, voidpf address)); + +struct internal_state; + +typedef struct z_stream_s { + Bytef *next_in; /* next input byte */ + uInt avail_in; /* number of bytes available at next_in */ + uLong total_in; /* total nb of input bytes read so far */ + + Bytef *next_out; /* next output byte should be put there */ + uInt avail_out; /* remaining free space at next_out */ + uLong total_out; /* total nb of bytes output so far */ + + char *msg; /* last error message, NULL if no error */ + struct internal_state FAR *state; /* not visible by applications */ + + alloc_func zalloc; /* used to allocate the internal state */ + free_func zfree; /* used to free the internal state */ + voidpf opaque; /* private data object passed to zalloc and zfree */ + + int data_type; /* best guess about the data type: binary or text */ + uLong adler; /* adler32 value of the uncompressed data */ + uLong reserved; /* reserved for future use */ +} z_stream; + +typedef z_stream FAR *z_streamp; + +/* + gzip header information passed to and from zlib routines. See RFC 1952 + for more details on the meanings of these fields. +*/ +typedef struct gz_header_s { + int text; /* true if compressed data believed to be text */ + uLong time; /* modification time */ + int xflags; /* extra flags (not used when writing a gzip file) */ + int os; /* operating system */ + Bytef *extra; /* pointer to extra field or Z_NULL if none */ + uInt extra_len; /* extra field length (valid if extra != Z_NULL) */ + uInt extra_max; /* space at extra (only when reading header) */ + Bytef *name; /* pointer to zero-terminated file name or Z_NULL */ + uInt name_max; /* space at name (only when reading header) */ + Bytef *comment; /* pointer to zero-terminated comment or Z_NULL */ + uInt comm_max; /* space at comment (only when reading header) */ + int hcrc; /* true if there was or will be a header crc */ + int done; /* true when done reading gzip header (not used + when writing a gzip file) */ +} gz_header; + +typedef gz_header FAR *gz_headerp; + +/* + The application must update next_in and avail_in when avail_in has + dropped to zero. It must update next_out and avail_out when avail_out + has dropped to zero. The application must initialize zalloc, zfree and + opaque before calling the init function. All other fields are set by the + compression library and must not be updated by the application. + + The opaque value provided by the application will be passed as the first + parameter for calls of zalloc and zfree. This can be useful for custom + memory management. The compression library attaches no meaning to the + opaque value. + + zalloc must return Z_NULL if there is not enough memory for the object. + If zlib is used in a multi-threaded application, zalloc and zfree must be + thread safe. + + On 16-bit systems, the functions zalloc and zfree must be able to allocate + exactly 65536 bytes, but will not be required to allocate more than this + if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS, + pointers returned by zalloc for objects of exactly 65536 bytes *must* + have their offset normalized to zero. The default allocation function + provided by this library ensures this (see zutil.c). To reduce memory + requirements and avoid any allocation of 64K objects, at the expense of + compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h). + + The fields total_in and total_out can be used for statistics or + progress reports. After compression, total_in holds the total size of + the uncompressed data and may be saved for use in the decompressor + (particularly if the decompressor wants to decompress everything in + a single step). +*/ + + /* constants */ + +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 /* will be removed, use Z_SYNC_FLUSH instead */ +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +/* Allowed flush values; see deflate() and inflate() below for details */ + +#define Z_OK 0 +#define Z_STREAM_END 1 +#define Z_NEED_DICT 2 +#define Z_ERRNO (-1) +#define Z_STREAM_ERROR (-2) +#define Z_DATA_ERROR (-3) +#define Z_MEM_ERROR (-4) +#define Z_BUF_ERROR (-5) +#define Z_VERSION_ERROR (-6) +/* Return codes for the compression/decompression functions. Negative + * values are errors, positive values are used for special but normal events. + */ + +#define Z_NO_COMPRESSION 0 +#define Z_BEST_SPEED 1 +#define Z_BEST_COMPRESSION 9 +#define Z_DEFAULT_COMPRESSION (-1) +/* compression levels */ + +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 +#define Z_DEFAULT_STRATEGY 0 +/* compression strategy; see deflateInit2() below for details */ + +#define Z_BINARY 0 +#define Z_TEXT 1 +#define Z_ASCII Z_TEXT /* for compatibility with 1.2.2 and earlier */ +#define Z_UNKNOWN 2 +/* Possible values of the data_type field (though see inflate()) */ + +#define Z_DEFLATED 8 +/* The deflate compression method (the only one supported in this version) */ + +#define Z_NULL 0 /* for initializing zalloc, zfree, opaque */ + +#define zlib_version zlibVersion() +/* for compatibility with versions < 1.0.2 */ + + /* basic functions */ + +ZEXTERN const char * ZEXPORT zlibVersion OF((void)); +/* The application can compare zlibVersion and ZLIB_VERSION for consistency. + If the first character differs, the library code actually used is + not compatible with the zlib.h header file used by the application. + This check is automatically made by deflateInit and inflateInit. + */ + +/* +ZEXTERN int ZEXPORT deflateInit OF((z_streamp strm, int level)); + + Initializes the internal stream state for compression. The fields + zalloc, zfree and opaque must be initialized before by the caller. + If zalloc and zfree are set to Z_NULL, deflateInit updates them to + use default allocation functions. + + The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: + 1 gives best speed, 9 gives best compression, 0 gives no compression at + all (the input data is simply copied a block at a time). + Z_DEFAULT_COMPRESSION requests a default compromise between speed and + compression (currently equivalent to level 6). + + deflateInit returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if level is not a valid compression level, + Z_VERSION_ERROR if the zlib library version (zlib_version) is incompatible + with the version assumed by the caller (ZLIB_VERSION). + msg is set to null if there is no error message. deflateInit does not + perform any compression: this will be done by deflate(). +*/ + + +ZEXTERN int ZEXPORT deflate OF((z_streamp strm, int flush)); +/* + deflate compresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce some + output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. deflate performs one or both of the + following actions: + + - Compress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in and avail_in are updated and + processing will resume at this point for the next call of deflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. This action is forced if the parameter flush is non zero. + Forcing flush frequently degrades the compression ratio, so this parameter + should be set only when necessary (in interactive applications). + Some output may be provided even if flush is not set. + + Before the call of deflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating avail_in or avail_out accordingly; avail_out + should never be zero before the call. The application can consume the + compressed output when it wants, for example when the output buffer is full + (avail_out == 0), or after each call of deflate(). If deflate returns Z_OK + and with zero avail_out, it must be called again after making room in the + output buffer because there might be more output pending. + + Normally the parameter flush is set to Z_NO_FLUSH, which allows deflate to + decide how much data to accumualte before producing output, in order to + maximize compression. + + If the parameter flush is set to Z_SYNC_FLUSH, all pending output is + flushed to the output buffer and the output is aligned on a byte boundary, so + that the decompressor can get all input data available so far. (In particular + avail_in is zero after the call if enough output space has been provided + before the call.) Flushing may degrade compression for some compression + algorithms and so it should be used only when necessary. + + If flush is set to Z_FULL_FLUSH, all output is flushed as with + Z_SYNC_FLUSH, and the compression state is reset so that decompression can + restart from this point if previous compressed data has been damaged or if + random access is desired. Using Z_FULL_FLUSH too often can seriously degrade + compression. + + If deflate returns with avail_out == 0, this function must be called again + with the same value of the flush parameter and more output space (updated + avail_out), until the flush is complete (deflate returns with non-zero + avail_out). In the case of a Z_FULL_FLUSH or Z_SYNC_FLUSH, make sure that + avail_out is greater than six to avoid repeated flush markers due to + avail_out == 0 on return. + + If the parameter flush is set to Z_FINISH, pending input is processed, + pending output is flushed and deflate returns with Z_STREAM_END if there + was enough output space; if deflate returns with Z_OK, this function must be + called again with Z_FINISH and more output space (updated avail_out) but no + more input data, until it returns with Z_STREAM_END or an error. After + deflate has returned Z_STREAM_END, the only possible operations on the + stream are deflateReset or deflateEnd. + + Z_FINISH can be used immediately after deflateInit if all the compression + is to be done in a single step. In this case, avail_out must be at least + the value returned by deflateBound (see below). If deflate does not return + Z_STREAM_END, then it must be called again as described above. + + deflate() sets strm->adler to the adler32 checksum of all input read + so far (that is, total_in bytes). + + deflate() may update strm->data_type if it can make a good guess about + the input data type (Z_BINARY or Z_TEXT). In doubt, the data is considered + binary. This field is only for information purposes and does not affect + the compression algorithm in any manner. + + deflate() returns Z_OK if some progress has been made (more input + processed or more output produced), Z_STREAM_END if all input has been + consumed and all output has been produced (only when flush is set to + Z_FINISH), Z_STREAM_ERROR if the stream state was inconsistent (for example + if next_in or next_out was NULL), Z_BUF_ERROR if no progress is possible + (for example avail_in or avail_out was zero). Note that Z_BUF_ERROR is not + fatal, and deflate() can be called again with more input and more output + space to continue compressing. +*/ + + +ZEXTERN int ZEXPORT deflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + deflateEnd returns Z_OK if success, Z_STREAM_ERROR if the + stream state was inconsistent, Z_DATA_ERROR if the stream was freed + prematurely (some input or output was discarded). In the error case, + msg may be set but then points to a static string (which must not be + deallocated). +*/ + + +/* +ZEXTERN int ZEXPORT inflateInit OF((z_streamp strm)); + + Initializes the internal stream state for decompression. The fields + next_in, avail_in, zalloc, zfree and opaque must be initialized before by + the caller. If next_in is not Z_NULL and avail_in is large enough (the exact + value depends on the compression method), inflateInit determines the + compression method from the zlib header and allocates all data structures + accordingly; otherwise the allocation will be deferred to the first call of + inflate. If zalloc and zfree are set to Z_NULL, inflateInit updates them to + use default allocation functions. + + inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_VERSION_ERROR if the zlib library version is incompatible with the + version assumed by the caller. msg is set to null if there is no error + message. inflateInit does not perform any decompression apart from reading + the zlib header if present: this will be done by inflate(). (So next_in and + avail_in may be modified, but next_out and avail_out are unchanged.) +*/ + + +ZEXTERN int ZEXPORT inflate OF((z_streamp strm, int flush)); +/* + inflate decompresses as much data as possible, and stops when the input + buffer becomes empty or the output buffer becomes full. It may introduce + some output latency (reading input without producing any output) except when + forced to flush. + + The detailed semantics are as follows. inflate performs one or both of the + following actions: + + - Decompress more input starting at next_in and update next_in and avail_in + accordingly. If not all input can be processed (because there is not + enough room in the output buffer), next_in is updated and processing + will resume at this point for the next call of inflate(). + + - Provide more output starting at next_out and update next_out and avail_out + accordingly. inflate() provides as much output as possible, until there + is no more input data or no more space in the output buffer (see below + about the flush parameter). + + Before the call of inflate(), the application should ensure that at least + one of the actions is possible, by providing more input and/or consuming + more output, and updating the next_* and avail_* values accordingly. + The application can consume the uncompressed output when it wants, for + example when the output buffer is full (avail_out == 0), or after each + call of inflate(). If inflate returns Z_OK and with zero avail_out, it + must be called again after making room in the output buffer because there + might be more output pending. + + The flush parameter of inflate() can be Z_NO_FLUSH, Z_SYNC_FLUSH, + Z_FINISH, or Z_BLOCK. Z_SYNC_FLUSH requests that inflate() flush as much + output as possible to the output buffer. Z_BLOCK requests that inflate() stop + if and when it gets to the next deflate block boundary. When decoding the + zlib or gzip format, this will cause inflate() to return immediately after + the header and before the first block. When doing a raw inflate, inflate() + will go ahead and process the first block, and will return when it gets to + the end of that block, or when it runs out of data. + + The Z_BLOCK option assists in appending to or combining deflate streams. + Also to assist in this, on return inflate() will set strm->data_type to the + number of unused bits in the last byte taken from strm->next_in, plus 64 + if inflate() is currently decoding the last block in the deflate stream, + plus 128 if inflate() returned immediately after decoding an end-of-block + code or decoding the complete header up to just before the first byte of the + deflate stream. The end-of-block will not be indicated until all of the + uncompressed data from that block has been written to strm->next_out. The + number of unused bits may in general be greater than seven, except when + bit 7 of data_type is set, in which case the number of unused bits will be + less than eight. + + inflate() should normally be called until it returns Z_STREAM_END or an + error. However if all decompression is to be performed in a single step + (a single call of inflate), the parameter flush should be set to + Z_FINISH. In this case all pending input is processed and all pending + output is flushed; avail_out must be large enough to hold all the + uncompressed data. (The size of the uncompressed data may have been saved + by the compressor for this purpose.) The next operation on this stream must + be inflateEnd to deallocate the decompression state. The use of Z_FINISH + is never required, but can be used to inform inflate that a faster approach + may be used for the single inflate() call. + + In this implementation, inflate() always flushes as much output as + possible to the output buffer, and always uses the faster approach on the + first call. So the only effect of the flush parameter in this implementation + is on the return value of inflate(), as noted below, or when it returns early + because Z_BLOCK is used. + + If a preset dictionary is needed after this call (see inflateSetDictionary + below), inflate sets strm->adler to the adler32 checksum of the dictionary + chosen by the compressor and returns Z_NEED_DICT; otherwise it sets + strm->adler to the adler32 checksum of all output produced so far (that is, + total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described + below. At the end of the stream, inflate() checks that its computed adler32 + checksum is equal to that saved by the compressor and returns Z_STREAM_END + only if the checksum is correct. + + inflate() will decompress and check either zlib-wrapped or gzip-wrapped + deflate data. The header type is detected automatically. Any information + contained in the gzip header is not retained, so applications that need that + information should instead use raw inflate, see inflateInit2() below, or + inflateBack() and perform their own processing of the gzip header and + trailer. + + inflate() returns Z_OK if some progress has been made (more input processed + or more output produced), Z_STREAM_END if the end of the compressed data has + been reached and all uncompressed output has been produced, Z_NEED_DICT if a + preset dictionary is needed at this point, Z_DATA_ERROR if the input data was + corrupted (input stream not conforming to the zlib format or incorrect check + value), Z_STREAM_ERROR if the stream structure was inconsistent (for example + if next_in or next_out was NULL), Z_MEM_ERROR if there was not enough memory, + Z_BUF_ERROR if no progress is possible or if there was not enough room in the + output buffer when Z_FINISH is used. Note that Z_BUF_ERROR is not fatal, and + inflate() can be called again with more input and more output space to + continue decompressing. If Z_DATA_ERROR is returned, the application may then + call inflateSync() to look for a good compression block if a partial recovery + of the data is desired. +*/ + + +ZEXTERN int ZEXPORT inflateEnd OF((z_streamp strm)); +/* + All dynamically allocated data structures for this stream are freed. + This function discards any unprocessed input and does not flush any + pending output. + + inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state + was inconsistent. In the error case, msg may be set but then points to a + static string (which must not be deallocated). +*/ + + /* Advanced functions */ + +/* + The following functions are needed only in some special applications. +*/ + +/* +ZEXTERN int ZEXPORT deflateInit2 OF((z_streamp strm, + int level, + int method, + int windowBits, + int memLevel, + int strategy)); + + This is another version of deflateInit with more compression options. The + fields next_in, zalloc, zfree and opaque must be initialized before by + the caller. + + The method parameter is the compression method. It must be Z_DEFLATED in + this version of the library. + + The windowBits parameter is the base two logarithm of the window size + (the size of the history buffer). It should be in the range 8..15 for this + version of the library. Larger values of this parameter result in better + compression at the expense of memory usage. The default value is 15 if + deflateInit is used instead. + + windowBits can also be -8..-15 for raw deflate. In this case, -windowBits + determines the window size. deflate() will then generate raw deflate data + with no zlib header or trailer, and will not compute an adler32 check value. + + windowBits can also be greater than 15 for optional gzip encoding. Add + 16 to windowBits to write a simple gzip header and trailer around the + compressed data instead of a zlib wrapper. The gzip header will have no + file name, no extra data, no comment, no modification time (set to zero), + no header crc, and the operating system will be set to 255 (unknown). If a + gzip stream is being written, strm->adler is a crc32 instead of an adler32. + + The memLevel parameter specifies how much memory should be allocated + for the internal compression state. memLevel=1 uses minimum memory but + is slow and reduces compression ratio; memLevel=9 uses maximum memory + for optimal speed. The default value is 8. See zconf.h for total memory + usage as a function of windowBits and memLevel. + + The strategy parameter is used to tune the compression algorithm. Use the + value Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data produced by a + filter (or predictor), Z_HUFFMAN_ONLY to force Huffman encoding only (no + string match), or Z_RLE to limit match distances to one (run-length + encoding). Filtered data consists mostly of small values with a somewhat + random distribution. In this case, the compression algorithm is tuned to + compress them better. The effect of Z_FILTERED is to force more Huffman + coding and less string matching; it is somewhat intermediate between + Z_DEFAULT and Z_HUFFMAN_ONLY. Z_RLE is designed to be almost as fast as + Z_HUFFMAN_ONLY, but give better compression for PNG image data. The strategy + parameter only affects the compression ratio but not the correctness of the + compressed output even if it is not set appropriately. Z_FIXED prevents the + use of dynamic Huffman codes, allowing for a simpler decoder for special + applications. + + deflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as an invalid + method). msg is set to null if there is no error message. deflateInit2 does + not perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the compression dictionary from the given byte sequence + without producing any compressed output. This function must be called + immediately after deflateInit, deflateInit2 or deflateReset, before any + call of deflate. The compressor and decompressor must use exactly the same + dictionary (see inflateSetDictionary). + + The dictionary should consist of strings (byte sequences) that are likely + to be encountered later in the data to be compressed, with the most commonly + used strings preferably put towards the end of the dictionary. Using a + dictionary is most useful when the data to be compressed is short and can be + predicted with good accuracy; the data can then be compressed better than + with the default empty dictionary. + + Depending on the size of the compression data structures selected by + deflateInit or deflateInit2, a part of the dictionary may in effect be + discarded, for example if the dictionary is larger than the window size in + deflate or deflate2. Thus the strings most likely to be useful should be + put at the end of the dictionary, not at the front. In addition, the + current implementation of deflate will use at most the window size minus + 262 bytes of the provided dictionary. + + Upon return of this function, strm->adler is set to the adler32 value + of the dictionary; the decompressor may later use this value to determine + which dictionary has been used by the compressor. (The adler32 value + applies to the whole dictionary even if only a subset of the dictionary is + actually used by the compressor.) If a raw deflate was requested, then the + adler32 value is not computed and strm->adler is not set. + + deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent (for example if deflate has already been called for this stream + or if the compression method is bsort). deflateSetDictionary does not + perform any compression: this will be done by deflate(). +*/ + +ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when several compression strategies will be + tried, for example when there are several ways of pre-processing the input + data with a filter. The streams that will be discarded should then be freed + by calling deflateEnd. Note that deflateCopy duplicates the internal + compression state which can be quite large, so this strategy is slow and + can consume lots of memory. + + deflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT deflateReset OF((z_streamp strm)); +/* + This function is equivalent to deflateEnd followed by deflateInit, + but does not free and reallocate all the internal compression state. + The stream will keep the same compression level and any other attributes + that may have been set by deflateInit2. + + deflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT deflateParams OF((z_streamp strm, + int level, + int strategy)); +/* + Dynamically update the compression level and compression strategy. The + interpretation of level and strategy is as in deflateInit2. This can be + used to switch between compression and straight copy of the input data, or + to switch to a different kind of input data requiring a different + strategy. If the compression level is changed, the input available so far + is compressed with the old level (and may be flushed); the new level will + take effect only at the next call of deflate(). + + Before the call of deflateParams, the stream state must be set as for + a call of deflate(), since the currently available input may have to + be compressed and flushed. In particular, strm->avail_out must be non-zero. + + deflateParams returns Z_OK if success, Z_STREAM_ERROR if the source + stream state was inconsistent or if a parameter was invalid, Z_BUF_ERROR + if strm->avail_out was zero. +*/ + +ZEXTERN int ZEXPORT deflateTune OF((z_streamp strm, + int good_length, + int max_lazy, + int nice_length, + int max_chain)); +/* + Fine tune deflate's internal compression parameters. This should only be + used by someone who understands the algorithm used by zlib's deflate for + searching for the best matching string, and even then only by the most + fanatic optimizer trying to squeeze out the last compressed bit for their + specific input data. Read the deflate.c source code for the meaning of the + max_lazy, good_length, nice_length, and max_chain parameters. + + deflateTune() can be called after deflateInit() or deflateInit2(), and + returns Z_OK on success, or Z_STREAM_ERROR for an invalid deflate stream. + */ + +ZEXTERN uLong ZEXPORT deflateBound OF((z_streamp strm, + uLong sourceLen)); +/* + deflateBound() returns an upper bound on the compressed size after + deflation of sourceLen bytes. It must be called after deflateInit() + or deflateInit2(). This would be used to allocate an output buffer + for deflation in a single pass, and so would be called before deflate(). +*/ + +ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + deflatePrime() inserts bits in the deflate output stream. The intent + is that this function is used to start off the deflate output with the + bits leftover from a previous deflate stream when appending to it. As such, + this function can only be used for raw deflate, and must be used before the + first deflate() call after a deflateInit2() or deflateReset(). bits must be + less than or equal to 16, and that many of the least significant bits of + value will be inserted in the output. + + deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, + gz_headerp head)); +/* + deflateSetHeader() provides gzip header information for when a gzip + stream is requested by deflateInit2(). deflateSetHeader() may be called + after deflateInit2() or deflateReset() and before the first call of + deflate(). The text, time, os, extra field, name, and comment information + in the provided gz_header structure are written to the gzip header (xflag is + ignored -- the extra flags are set according to the compression level). The + caller must assure that, if not Z_NULL, name and comment are terminated with + a zero byte, and that if extra is not Z_NULL, that extra_len bytes are + available there. If hcrc is true, a gzip header crc is included. Note that + the current versions of the command-line version of gzip (up through version + 1.3.x) do not support header crc's, and will report that it is a "multi-part + gzip file" and give up. + + If deflateSetHeader is not used, the default gzip header has text false, + the time set to zero, and os set to 255, with no extra, name, or comment + fields. The gzip header is returned to the default state by deflateReset(). + + deflateSetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateInit2 OF((z_streamp strm, + int windowBits)); + + This is another version of inflateInit with an extra parameter. The + fields next_in, avail_in, zalloc, zfree and opaque must be initialized + before by the caller. + + The windowBits parameter is the base two logarithm of the maximum window + size (the size of the history buffer). It should be in the range 8..15 for + this version of the library. The default value is 15 if inflateInit is used + instead. windowBits must be greater than or equal to the windowBits value + provided to deflateInit2() while compressing, or it must be equal to 15 if + deflateInit2() was not used. If a compressed stream with a larger window + size is given as input, inflate() will return with the error code + Z_DATA_ERROR instead of trying to allocate a larger window. + + windowBits can also be -8..-15 for raw inflate. In this case, -windowBits + determines the window size. inflate() will then process raw deflate data, + not looking for a zlib or gzip header, not generating a check value, and not + looking for any check values for comparison at the end of the stream. This + is for use with other formats that use the deflate compressed data format + such as zip. Those formats provide their own check values. If a custom + format is developed using the raw deflate format for compressed data, it is + recommended that a check value such as an adler32 or a crc32 be applied to + the uncompressed data as is done in the zlib, gzip, and zip formats. For + most applications, the zlib format should be used as is. Note that comments + above on the use in deflateInit2() applies to the magnitude of windowBits. + + windowBits can also be greater than 15 for optional gzip decoding. Add + 32 to windowBits to enable zlib and gzip decoding with automatic header + detection, or add 16 to decode only the gzip format (the zlib format will + return a Z_DATA_ERROR). If a gzip stream is being decoded, strm->adler is + a crc32 instead of an adler32. + + inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_STREAM_ERROR if a parameter is invalid (such as a null strm). msg + is set to null if there is no error message. inflateInit2 does not perform + any decompression apart from reading the zlib header if present: this will + be done by inflate(). (So next_in and avail_in may be modified, but next_out + and avail_out are unchanged.) +*/ + +ZEXTERN int ZEXPORT inflateSetDictionary OF((z_streamp strm, + const Bytef *dictionary, + uInt dictLength)); +/* + Initializes the decompression dictionary from the given uncompressed byte + sequence. This function must be called immediately after a call of inflate, + if that call returned Z_NEED_DICT. The dictionary chosen by the compressor + can be determined from the adler32 value returned by that call of inflate. + The compressor and decompressor must use exactly the same dictionary (see + deflateSetDictionary). For raw inflate, this function can be called + immediately after inflateInit2() or inflateReset() and before any call of + inflate() to set the dictionary. The application must insure that the + dictionary that was used for compression is provided. + + inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a + parameter is invalid (such as NULL dictionary) or the stream state is + inconsistent, Z_DATA_ERROR if the given dictionary doesn't match the + expected one (incorrect adler32 value). inflateSetDictionary does not + perform any decompression: this will be done by subsequent calls of + inflate(). +*/ + +ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); +/* + Skips invalid compressed data until a full flush point (see above the + description of deflate with Z_FULL_FLUSH) can be found, or until all + available input is skipped. No output is provided. + + inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR + if no more input was provided, Z_DATA_ERROR if no flush point has been found, + or Z_STREAM_ERROR if the stream structure was inconsistent. In the success + case, the application may save the current current value of total_in which + indicates where valid compressed data was found. In the error case, the + application may repeatedly call inflateSync, providing more input each time, + until success or end of the input data. +*/ + +ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, + z_streamp source)); +/* + Sets the destination stream as a complete copy of the source stream. + + This function can be useful when randomly accessing a large stream. The + first pass through the stream can periodically record the inflate state, + allowing restarting inflate at those points when randomly accessing the + stream. + + inflateCopy returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_STREAM_ERROR if the source stream state was inconsistent + (such as zalloc being NULL). msg is left unchanged in both source and + destination. +*/ + +ZEXTERN int ZEXPORT inflateReset OF((z_streamp strm)); +/* + This function is equivalent to inflateEnd followed by inflateInit, + but does not free and reallocate all the internal decompression state. + The stream will keep attributes that may have been set by inflateInit2. + + inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent (such as zalloc or state being NULL). +*/ + +ZEXTERN int ZEXPORT inflatePrime OF((z_streamp strm, + int bits, + int value)); +/* + This function inserts bits in the inflate input stream. The intent is + that this function is used to start inflating at a bit position in the + middle of a byte. The provided bits will be used before any bytes are used + from next_in. This function should only be used with raw inflate, and + should be used before the first inflate() call after inflateInit2() or + inflateReset(). bits must be less than or equal to 16, and that many of the + least significant bits of value will be inserted in the input. + + inflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +ZEXTERN int ZEXPORT inflateGetHeader OF((z_streamp strm, + gz_headerp head)); +/* + inflateGetHeader() requests that gzip header information be stored in the + provided gz_header structure. inflateGetHeader() may be called after + inflateInit2() or inflateReset(), and before the first call of inflate(). + As inflate() processes the gzip stream, head->done is zero until the header + is completed, at which time head->done is set to one. If a zlib stream is + being decoded, then head->done is set to -1 to indicate that there will be + no gzip header information forthcoming. Note that Z_BLOCK can be used to + force inflate() to return immediately after header processing is complete + and before any actual data is decompressed. + + The text, time, xflags, and os fields are filled in with the gzip header + contents. hcrc is set to true if there is a header CRC. (The header CRC + was valid if done is set to one.) If extra is not Z_NULL, then extra_max + contains the maximum number of bytes to write to extra. Once done is true, + extra_len contains the actual extra field length, and extra contains the + extra field, or that field truncated if extra_max is less than extra_len. + If name is not Z_NULL, then up to name_max characters are written there, + terminated with a zero unless the length is greater than name_max. If + comment is not Z_NULL, then up to comm_max characters are written there, + terminated with a zero unless the length is greater than comm_max. When + any of extra, name, or comment are not Z_NULL and the respective field is + not present in the header, then that field is set to Z_NULL to signal its + absence. This allows the use of deflateSetHeader() with the returned + structure to duplicate the header. However if those fields are set to + allocated memory, then the application will need to save those pointers + elsewhere so that they can be eventually freed. + + If inflateGetHeader is not used, then the header information is simply + discarded. The header is always checked for validity, including the header + CRC if present. inflateReset() will reset the process to discard the header + information. The application would need to call inflateGetHeader() again to + retrieve the header from the next gzip stream. + + inflateGetHeader returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. +*/ + +/* +ZEXTERN int ZEXPORT inflateBackInit OF((z_streamp strm, int windowBits, + unsigned char FAR *window)); + + Initialize the internal stream state for decompression using inflateBack() + calls. The fields zalloc, zfree and opaque in strm must be initialized + before the call. If zalloc and zfree are Z_NULL, then the default library- + derived memory allocation routines are used. windowBits is the base two + logarithm of the window size, in the range 8..15. window is a caller + supplied buffer of that size. Except for special applications where it is + assured that deflate was used with small window sizes, windowBits must be 15 + and a 32K byte window must be supplied to be able to decompress general + deflate streams. + + See inflateBack() for the usage of these routines. + + inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of + the paramaters are invalid, Z_MEM_ERROR if the internal state could not + be allocated, or Z_VERSION_ERROR if the version of the library does not + match the version of the header file. +*/ + +typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); + +ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, + in_func in, void FAR *in_desc, + out_func out, void FAR *out_desc)); +/* + inflateBack() does a raw inflate with a single call using a call-back + interface for input and output. This is more efficient than inflate() for + file i/o applications in that it avoids copying between the output and the + sliding window by simply making the window itself the output buffer. This + function trusts the application to not change the output buffer passed by + the output function, at least until inflateBack() returns. + + inflateBackInit() must be called first to allocate the internal state + and to initialize the state with the user-provided window buffer. + inflateBack() may then be used multiple times to inflate a complete, raw + deflate stream with each call. inflateBackEnd() is then called to free + the allocated state. + + A raw deflate stream is one with no zlib or gzip header or trailer. + This routine would normally be used in a utility that reads zip or gzip + files and writes out uncompressed files. The utility would decode the + header and process the trailer on its own, hence this routine expects + only the raw deflate stream to decompress. This is different from the + normal behavior of inflate(), which expects either a zlib or gzip header and + trailer around the deflate stream. + + inflateBack() uses two subroutines supplied by the caller that are then + called by inflateBack() for input and output. inflateBack() calls those + routines until it reads a complete deflate stream and writes out all of the + uncompressed data, or until it encounters an error. The function's + parameters and return types are defined above in the in_func and out_func + typedefs. inflateBack() will call in(in_desc, &buf) which should return the + number of bytes of provided input, and a pointer to that input in buf. If + there is no input available, in() must return zero--buf is ignored in that + case--and inflateBack() will return a buffer error. inflateBack() will call + out(out_desc, buf, len) to write the uncompressed data buf[0..len-1]. out() + should return zero on success, or non-zero on failure. If out() returns + non-zero, inflateBack() will return with an error. Neither in() nor out() + are permitted to change the contents of the window provided to + inflateBackInit(), which is also the buffer that out() uses to write from. + The length written by out() will be at most the window size. Any non-zero + amount of input may be provided by in(). + + For convenience, inflateBack() can be provided input on the first call by + setting strm->next_in and strm->avail_in. If that input is exhausted, then + in() will be called. Therefore strm->next_in must be initialized before + calling inflateBack(). If strm->next_in is Z_NULL, then in() will be called + immediately for input. If strm->next_in is not Z_NULL, then strm->avail_in + must also be initialized, and then if strm->avail_in is not zero, input will + initially be taken from strm->next_in[0 .. strm->avail_in - 1]. + + The in_desc and out_desc parameters of inflateBack() is passed as the + first parameter of in() and out() respectively when they are called. These + descriptors can be optionally used to pass any information that the caller- + supplied in() and out() functions need to do their job. + + On return, inflateBack() will set strm->next_in and strm->avail_in to + pass back any unused input that was provided by the last in() call. The + return values of inflateBack() can be Z_STREAM_END on success, Z_BUF_ERROR + if in() or out() returned an error, Z_DATA_ERROR if there was a format + error in the deflate stream (in which case strm->msg is set to indicate the + nature of the error), or Z_STREAM_ERROR if the stream was not properly + initialized. In the case of Z_BUF_ERROR, an input or output error can be + distinguished using strm->next_in which will be Z_NULL only if in() returned + an error. If strm->next is not Z_NULL, then the Z_BUF_ERROR was due to + out() returning non-zero. (in() will always be called before out(), so + strm->next_in is assured to be defined if out() returns non-zero.) Note + that inflateBack() cannot return Z_OK. +*/ + +ZEXTERN int ZEXPORT inflateBackEnd OF((z_streamp strm)); +/* + All memory allocated by inflateBackInit() is freed. + + inflateBackEnd() returns Z_OK on success, or Z_STREAM_ERROR if the stream + state was inconsistent. +*/ + +ZEXTERN uLong ZEXPORT zlibCompileFlags OF((void)); +/* Return flags indicating compile-time options. + + Type sizes, two bits each, 00 = 16 bits, 01 = 32, 10 = 64, 11 = other: + 1.0: size of uInt + 3.2: size of uLong + 5.4: size of voidpf (pointer) + 7.6: size of z_off_t + + Compiler, assembler, and debug options: + 8: DEBUG + 9: ASMV or ASMINF -- use ASM code + 10: ZLIB_WINAPI -- exported functions use the WINAPI calling convention + 11: 0 (reserved) + + One-time table building (smaller code, but not thread-safe if true): + 12: BUILDFIXED -- build static block decoding tables when needed + 13: DYNAMIC_CRC_TABLE -- build CRC calculation tables when needed + 14,15: 0 (reserved) + + Library content (indicates missing functionality): + 16: NO_GZCOMPRESS -- gz* functions cannot compress (to avoid linking + deflate code when not needed) + 17: NO_GZIP -- deflate can't write gzip streams, and inflate can't detect + and decode gzip streams (to avoid linking crc code) + 18-19: 0 (reserved) + + Operation variations (changes in library functionality): + 20: PKZIP_BUG_WORKAROUND -- slightly more permissive inflate + 21: FASTEST -- deflate algorithm with only one, lowest compression level + 22,23: 0 (reserved) + + The sprintf variant used by gzprintf (zero is best): + 24: 0 = vs*, 1 = s* -- 1 means limited to 20 arguments after the format + 25: 0 = *nprintf, 1 = *printf -- 1 means gzprintf() not secure! + 26: 0 = returns value, 1 = void -- 1 means inferred string length returned + + Remainder: + 27-31: 0 (reserved) + */ + + + /* utility functions */ + +/* + The following utility functions are implemented on top of the + basic stream-oriented functions. To simplify the interface, some + default options are assumed (compression level and memory usage, + standard memory allocation functions). The source code of these + utility functions can easily be modified if you need special options. +*/ + +ZEXTERN int ZEXPORT compress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Compresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be at least the value returned + by compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + This function can be used to compress a whole file at once if the + input file is mmap'ed. + compress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer. +*/ + +ZEXTERN int ZEXPORT compress2 OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen, + int level)); +/* + Compresses the source buffer into the destination buffer. The level + parameter has the same meaning as in deflateInit. sourceLen is the byte + length of the source buffer. Upon entry, destLen is the total size of the + destination buffer, which must be at least the value returned by + compressBound(sourceLen). Upon exit, destLen is the actual size of the + compressed buffer. + + compress2 returns Z_OK if success, Z_MEM_ERROR if there was not enough + memory, Z_BUF_ERROR if there was not enough room in the output buffer, + Z_STREAM_ERROR if the level parameter is invalid. +*/ + +ZEXTERN uLong ZEXPORT compressBound OF((uLong sourceLen)); +/* + compressBound() returns an upper bound on the compressed size after + compress() or compress2() on sourceLen bytes. It would be used before + a compress() or compress2() call to allocate the destination buffer. +*/ + +ZEXTERN int ZEXPORT uncompress OF((Bytef *dest, uLongf *destLen, + const Bytef *source, uLong sourceLen)); +/* + Decompresses the source buffer into the destination buffer. sourceLen is + the byte length of the source buffer. Upon entry, destLen is the total + size of the destination buffer, which must be large enough to hold the + entire uncompressed data. (The size of the uncompressed data must have + been saved previously by the compressor and transmitted to the decompressor + by some mechanism outside the scope of this compression library.) + Upon exit, destLen is the actual size of the compressed buffer. + This function can be used to decompress a whole file at once if the + input file is mmap'ed. + + uncompress returns Z_OK if success, Z_MEM_ERROR if there was not + enough memory, Z_BUF_ERROR if there was not enough room in the output + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. +*/ + + +typedef voidp gzFile; + +ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); +/* + Opens a gzip (.gz) file for reading or writing. The mode parameter + is as in fopen ("rb" or "wb") but can also include a compression level + ("wb9") or a strategy: 'f' for filtered data as in "wb6f", 'h' for + Huffman only compression as in "wb1h", or 'R' for run-length encoding + as in "wb1R". (See the description of deflateInit2 for more information + about the strategy parameter.) + + gzopen can be used to read a file which is not in gzip format; in this + case gzread will directly read from the file without decompression. + + gzopen returns NULL if the file could not be opened or if there was + insufficient memory to allocate the (de)compression state; errno + can be checked to distinguish the two cases (if errno is zero, the + zlib error is Z_MEM_ERROR). */ + +ZEXTERN gzFile ZEXPORT gzdopen OF((int fd, const char *mode)); +/* + gzdopen() associates a gzFile with the file descriptor fd. File + descriptors are obtained from calls like open, dup, creat, pipe or + fileno (in the file has been previously opened with fopen). + The mode parameter is as in gzopen. + The next call of gzclose on the returned gzFile will also close the + file descriptor fd, just like fclose(fdopen(fd), mode) closes the file + descriptor fd. If you want to keep fd open, use gzdopen(dup(fd), mode). + gzdopen returns NULL if there was insufficient memory to allocate + the (de)compression state. +*/ + +ZEXTERN int ZEXPORT gzsetparams OF((gzFile file, int level, int strategy)); +/* + Dynamically update the compression level or strategy. See the description + of deflateInit2 for the meaning of these parameters. + gzsetparams returns Z_OK if success, or Z_STREAM_ERROR if the file was not + opened for writing. +*/ + +ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); +/* + Reads the given number of uncompressed bytes from the compressed file. + If the input file was not in gzip format, gzread copies the given number + of bytes into the buffer. + gzread returns the number of uncompressed bytes actually read (0 for + end of file, -1 for error). */ + +ZEXTERN int ZEXPORT gzwrite OF((gzFile file, + voidpc buf, unsigned len)); +/* + Writes the given number of uncompressed bytes into the compressed file. + gzwrite returns the number of uncompressed bytes actually written + (0 in case of error). +*/ + +ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +/* + Converts, formats, and writes the args to the compressed file under + control of the format string, as in fprintf. gzprintf returns the number of + uncompressed bytes actually written (0 in case of error). The number of + uncompressed bytes written is limited to 4095. The caller should assure that + this limit is not exceeded. If it is exceeded, then gzprintf() will return + return an error (0) with nothing written. In this case, there may also be a + buffer overflow with unpredictable consequences, which is possible only if + zlib was compiled with the insecure functions sprintf() or vsprintf() + because the secure snprintf() or vsnprintf() functions were not available. +*/ + +ZEXTERN int ZEXPORT gzputs OF((gzFile file, const char *s)); +/* + Writes the given null-terminated string to the compressed file, excluding + the terminating null character. + gzputs returns the number of characters written, or -1 in case of error. +*/ + +ZEXTERN char * ZEXPORT gzgets OF((gzFile file, char *buf, int len)); +/* + Reads bytes from the compressed file until len-1 characters are read, or + a newline character is read and transferred to buf, or an end-of-file + condition is encountered. The string is then terminated with a null + character. + gzgets returns buf, or Z_NULL in case of error. +*/ + +ZEXTERN int ZEXPORT gzputc OF((gzFile file, int c)); +/* + Writes c, converted to an unsigned char, into the compressed file. + gzputc returns the value that was written, or -1 in case of error. +*/ + +ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); +/* + Reads one byte from the compressed file. gzgetc returns this byte + or -1 in case of end of file or error. +*/ + +ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); +/* + Push one character back onto the stream to be read again later. + Only one character of push-back is allowed. gzungetc() returns the + character pushed, or -1 on failure. gzungetc() will fail if a + character has been pushed but not read yet, or if c is -1. The pushed + character will be discarded if the stream is repositioned with gzseek() + or gzrewind(). +*/ + +ZEXTERN int ZEXPORT gzflush OF((gzFile file, int flush)); +/* + Flushes all pending output into the compressed file. The parameter + flush is as in the deflate() function. The return value is the zlib + error number (see function gzerror below). gzflush returns Z_OK if + the flush parameter is Z_FINISH and all output could be flushed. + gzflush should be called only when strictly necessary because it can + degrade compression. +*/ + +ZEXTERN z_off_t ZEXPORT gzseek OF((gzFile file, + z_off_t offset, int whence)); +/* + Sets the starting position for the next gzread or gzwrite on the + given compressed file. The offset represents a number of bytes in the + uncompressed data stream. The whence parameter is defined as in lseek(2); + the value SEEK_END is not supported. + If the file is opened for reading, this function is emulated but can be + extremely slow. If the file is opened for writing, only forward seeks are + supported; gzseek then compresses a sequence of zeroes up to the new + starting position. + + gzseek returns the resulting offset location as measured in bytes from + the beginning of the uncompressed stream, or -1 in case of error, in + particular if the file is opened for writing and the new starting position + would be before the current position. +*/ + +ZEXTERN int ZEXPORT gzrewind OF((gzFile file)); +/* + Rewinds the given file. This function is supported only for reading. + + gzrewind(file) is equivalent to (int)gzseek(file, 0L, SEEK_SET) +*/ + +ZEXTERN z_off_t ZEXPORT gztell OF((gzFile file)); +/* + Returns the starting position for the next gzread or gzwrite on the + given compressed file. This position represents a number of bytes in the + uncompressed data stream. + + gztell(file) is equivalent to gzseek(file, 0L, SEEK_CUR) +*/ + +ZEXTERN int ZEXPORT gzeof OF((gzFile file)); +/* + Returns 1 when EOF has previously been detected reading the given + input stream, otherwise zero. +*/ + +ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); +/* + Returns 1 if file is being read directly without decompression, otherwise + zero. +*/ + +ZEXTERN int ZEXPORT gzclose OF((gzFile file)); +/* + Flushes all pending output if necessary, closes the compressed file + and deallocates all the (de)compression state. The return value is the zlib + error number (see function gzerror below). +*/ + +ZEXTERN const char * ZEXPORT gzerror OF((gzFile file, int *errnum)); +/* + Returns the error message for the last error which occurred on the + given compressed file. errnum is set to zlib error number. If an + error occurred in the file system and not in the compression library, + errnum is set to Z_ERRNO and the application may consult errno + to get the exact error code. +*/ + +ZEXTERN void ZEXPORT gzclearerr OF((gzFile file)); +/* + Clears the error and end-of-file flags for file. This is analogous to the + clearerr() function in stdio. This is useful for continuing to read a gzip + file that is being written concurrently. +*/ + + /* checksum functions */ + +/* + These functions are not related to compression but are exported + anyway because they might be useful in applications using the + compression library. +*/ + +ZEXTERN uLong ZEXPORT adler32 OF((uLong adler, const Bytef *buf, uInt len)); +/* + Update a running Adler-32 checksum with the bytes buf[0..len-1] and + return the updated checksum. If buf is NULL, this function returns + the required initial value for the checksum. + An Adler-32 checksum is almost as reliable as a CRC32 but can be computed + much faster. Usage example: + + uLong adler = adler32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + adler = adler32(adler, buffer, length); + } + if (adler != original_adler) error(); +*/ + +ZEXTERN uLong ZEXPORT adler32_combine OF((uLong adler1, uLong adler2, + z_off_t len2)); +/* + Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 + and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for + each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. +*/ + +ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); +/* + Update a running CRC-32 with the bytes buf[0..len-1] and return the + updated CRC-32. If buf is NULL, this function returns the required initial + value for the for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. + Usage example: + + uLong crc = crc32(0L, Z_NULL, 0); + + while (read_buffer(buffer, length) != EOF) { + crc = crc32(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ + +ZEXTERN uLong ZEXPORT crc32_combine OF((uLong crc1, uLong crc2, z_off_t len2)); + +/* + Combine two CRC-32 check values into one. For two sequences of bytes, + seq1 and seq2 with lengths len1 and len2, CRC-32 check values were + calculated for each, crc1 and crc2. crc32_combine() returns the CRC-32 + check value of seq1 and seq2 concatenated, requiring only crc1, crc2, and + len2. +*/ + + + /* various hacks, don't look :) */ + +/* deflateInit and inflateInit are macros to allow checking the zlib version + * and the compiler's view of z_stream: + */ +ZEXTERN int ZEXPORT deflateInit_ OF((z_streamp strm, int level, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateInit_ OF((z_streamp strm, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT deflateInit2_ OF((z_streamp strm, int level, int method, + int windowBits, int memLevel, + int strategy, const char *version, + int stream_size)); +ZEXTERN int ZEXPORT inflateInit2_ OF((z_streamp strm, int windowBits, + const char *version, int stream_size)); +ZEXTERN int ZEXPORT inflateBackInit_ OF((z_streamp strm, int windowBits, + unsigned char FAR *window, + const char *version, + int stream_size)); +#define deflateInit(strm, level) \ + deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit(strm) \ + inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) +#define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ + deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ + (strategy), ZLIB_VERSION, sizeof(z_stream)) +#define inflateInit2(strm, windowBits) \ + inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) +#define inflateBackInit(strm, windowBits, window) \ + inflateBackInit_((strm), (windowBits), (window), \ + ZLIB_VERSION, sizeof(z_stream)) + + +#if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) + struct internal_state {int dummy;}; /* hack for buggy compilers */ +#endif + +ZEXTERN const char * ZEXPORT zError OF((int)); +ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp z)); +ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); + +#ifdef __cplusplus +} +#endif + +#endif /* ZLIB_H */ diff --git a/src/lib/zlib/zutil.c b/src/lib/zlib/zutil.c new file mode 100644 index 00000000000..d55f5948a37 --- /dev/null +++ b/src/lib/zlib/zutil.c @@ -0,0 +1,318 @@ +/* zutil.c -- target dependent utility functions for the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* @(#) $Id$ */ + +#include "zutil.h" + +#ifndef NO_DUMMY_DECL +struct internal_state {int dummy;}; /* for buggy compilers */ +#endif + +const char * const z_errmsg[10] = { +"need dictionary", /* Z_NEED_DICT 2 */ +"stream end", /* Z_STREAM_END 1 */ +"", /* Z_OK 0 */ +"file error", /* Z_ERRNO (-1) */ +"stream error", /* Z_STREAM_ERROR (-2) */ +"data error", /* Z_DATA_ERROR (-3) */ +"insufficient memory", /* Z_MEM_ERROR (-4) */ +"buffer error", /* Z_BUF_ERROR (-5) */ +"incompatible version",/* Z_VERSION_ERROR (-6) */ +""}; + + +const char * ZEXPORT zlibVersion() +{ + return ZLIB_VERSION; +} + +uLong ZEXPORT zlibCompileFlags() +{ + uLong flags; + + flags = 0; + switch (sizeof(uInt)) { + case 2: break; + case 4: flags += 1; break; + case 8: flags += 2; break; + default: flags += 3; + } + switch (sizeof(uLong)) { + case 2: break; + case 4: flags += 1 << 2; break; + case 8: flags += 2 << 2; break; + default: flags += 3 << 2; + } + switch (sizeof(voidpf)) { + case 2: break; + case 4: flags += 1 << 4; break; + case 8: flags += 2 << 4; break; + default: flags += 3 << 4; + } + switch (sizeof(z_off_t)) { + case 2: break; + case 4: flags += 1 << 6; break; + case 8: flags += 2 << 6; break; + default: flags += 3 << 6; + } +#ifdef DEBUG + flags += 1 << 8; +#endif +#if defined(ASMV) || defined(ASMINF) + flags += 1 << 9; +#endif +#ifdef ZLIB_WINAPI + flags += 1 << 10; +#endif +#ifdef BUILDFIXED + flags += 1 << 12; +#endif +#ifdef DYNAMIC_CRC_TABLE + flags += 1 << 13; +#endif +#ifdef NO_GZCOMPRESS + flags += 1L << 16; +#endif +#ifdef NO_GZIP + flags += 1L << 17; +#endif +#ifdef PKZIP_BUG_WORKAROUND + flags += 1L << 20; +#endif +#ifdef FASTEST + flags += 1L << 21; +#endif +#ifdef STDC +# ifdef NO_vsnprintf + flags += 1L << 25; +# ifdef HAS_vsprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_vsnprintf_void + flags += 1L << 26; +# endif +# endif +#else + flags += 1L << 24; +# ifdef NO_snprintf + flags += 1L << 25; +# ifdef HAS_sprintf_void + flags += 1L << 26; +# endif +# else +# ifdef HAS_snprintf_void + flags += 1L << 26; +# endif +# endif +#endif + return flags; +} + +#ifdef DEBUG + +# ifndef verbose +# define verbose 0 +# endif +int z_verbose = verbose; + +void z_error (m) + char *m; +{ + fprintf(stderr, "%s\n", m); + exit(1); +} +#endif + +/* exported to allow conversion of error code to string for compress() and + * uncompress() + */ +const char * ZEXPORT zError(err) + int err; +{ + return ERR_MSG(err); +} + +#if defined(_WIN32_WCE) + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. + */ + int errno = 0; +#endif + +#ifndef HAVE_MEMCPY + +void zmemcpy(dest, source, len) + Bytef* dest; + const Bytef* source; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = *source++; /* ??? to be unrolled */ + } while (--len != 0); +} + +int zmemcmp(s1, s2, len) + const Bytef* s1; + const Bytef* s2; + uInt len; +{ + uInt j; + + for (j = 0; j < len; j++) { + if (s1[j] != s2[j]) return 2*(s1[j] > s2[j])-1; + } + return 0; +} + +void zmemzero(dest, len) + Bytef* dest; + uInt len; +{ + if (len == 0) return; + do { + *dest++ = 0; /* ??? to be unrolled */ + } while (--len != 0); +} +#endif + + +#ifdef SYS16BIT + +#ifdef __TURBOC__ +/* Turbo C in 16-bit mode */ + +# define MY_ZCALLOC + +/* Turbo C malloc() does not allow dynamic allocation of 64K bytes + * and farmalloc(64K) returns a pointer with an offset of 8, so we + * must fix the pointer. Warning: the pointer must be put back to its + * original form in order to free it, use zcfree(). + */ + +#define MAX_PTR 10 +/* 10*64K = 640K */ + +local int next_ptr = 0; + +typedef struct ptr_table_s { + voidpf org_ptr; + voidpf new_ptr; +} ptr_table; + +local ptr_table table[MAX_PTR]; +/* This table is used to remember the original form of pointers + * to large buffers (64K). Such pointers are normalized with a zero offset. + * Since MSDOS is not a preemptive multitasking OS, this table is not + * protected from concurrent access. This hack doesn't work anyway on + * a protected system like OS/2. Use Microsoft C instead. + */ + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + voidpf buf = opaque; /* just to make some compilers happy */ + ulg bsize = (ulg)items*size; + + /* If we allocate less than 65520 bytes, we assume that farmalloc + * will return a usable pointer which doesn't have to be normalized. + */ + if (bsize < 65520L) { + buf = farmalloc(bsize); + if (*(ush*)&buf != 0) return buf; + } else { + buf = farmalloc(bsize + 16L); + } + if (buf == NULL || next_ptr >= MAX_PTR) return NULL; + table[next_ptr].org_ptr = buf; + + /* Normalize the pointer to seg:0 */ + *((ush*)&buf+1) += ((ush)((uch*)buf-0) + 15) >> 4; + *(ush*)&buf = 0; + table[next_ptr++].new_ptr = buf; + return buf; +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + int n; + if (*(ush*)&ptr != 0) { /* object < 64K */ + farfree(ptr); + return; + } + /* Find the original pointer */ + for (n = 0; n < next_ptr; n++) { + if (ptr != table[n].new_ptr) continue; + + farfree(table[n].org_ptr); + while (++n < next_ptr) { + table[n-1] = table[n]; + } + next_ptr--; + return; + } + ptr = opaque; /* just to make some compilers happy */ + Assert(0, "zcfree: ptr not found"); +} + +#endif /* __TURBOC__ */ + + +#ifdef M_I86 +/* Microsoft C in 16-bit mode */ + +# define MY_ZCALLOC + +#if (!defined(_MSC_VER) || (_MSC_VER <= 600)) +# define _halloc halloc +# define _hfree hfree +#endif + +voidpf zcalloc (voidpf opaque, unsigned items, unsigned size) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + return _halloc((long)items, size); +} + +void zcfree (voidpf opaque, voidpf ptr) +{ + if (opaque) opaque = 0; /* to make compiler happy */ + _hfree(ptr); +} + +#endif /* M_I86 */ + +#endif /* SYS16BIT */ + + +#ifndef MY_ZCALLOC /* Any system without a special alloc function */ + +#ifndef STDC +extern voidp malloc OF((uInt size)); +extern voidp calloc OF((uInt items, uInt size)); +extern void free OF((voidpf ptr)); +#endif + +voidpf zcalloc (opaque, items, size) + voidpf opaque; + unsigned items; + unsigned size; +{ + if (opaque) items += size - size; /* make compiler happy */ + return sizeof(uInt) > 2 ? (voidpf)malloc(items * size) : + (voidpf)calloc(items, size); +} + +void zcfree (opaque, ptr) + voidpf opaque; + voidpf ptr; +{ + free(ptr); + if (opaque) return; /* make compiler happy */ +} + +#endif /* MY_ZCALLOC */ diff --git a/src/lib/zlib/zutil.h b/src/lib/zlib/zutil.h new file mode 100644 index 00000000000..b7d5eff81b6 --- /dev/null +++ b/src/lib/zlib/zutil.h @@ -0,0 +1,269 @@ +/* zutil.h -- internal interface and configuration of the compression library + * Copyright (C) 1995-2005 Jean-loup Gailly. + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* WARNING: this file should *not* be used by applications. It is + part of the implementation of the compression library and is + subject to change. Applications should only use zlib.h. + */ + +/* @(#) $Id$ */ + +#ifndef ZUTIL_H +#define ZUTIL_H + +#define ZLIB_INTERNAL +#include "zlib.h" + +#ifdef STDC +# ifndef _WIN32_WCE +# include <stddef.h> +# endif +# include <string.h> +# include <stdlib.h> +#endif +#ifdef NO_ERRNO_H +# ifdef _WIN32_WCE + /* The Microsoft C Run-Time Library for Windows CE doesn't have + * errno. We define it as a global variable to simplify porting. + * Its value is always 0 and should not be used. We rename it to + * avoid conflict with other libraries that use the same workaround. + */ +# define errno z_errno +# endif + extern int errno; +#else +# ifndef _WIN32_WCE +# include <errno.h> +# endif +#endif + +#ifndef local +# define local static +#endif +/* compile with -Dlocal if your debugger can't find static symbols */ + +typedef unsigned char uch; +typedef uch FAR uchf; +typedef unsigned short ush; +typedef ush FAR ushf; +typedef unsigned long ulg; + +extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +/* (size given to avoid silly warnings with Visual C++) */ + +#define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] + +#define ERR_RETURN(strm,err) \ + return (strm->msg = (char*)ERR_MSG(err), (err)) +/* To be used only when the state is known to be valid */ + + /* common constants */ + +#ifndef DEF_WBITS +# define DEF_WBITS MAX_WBITS +#endif +/* default windowBits for decompression. MAX_WBITS is for compression only */ + +#if MAX_MEM_LEVEL >= 8 +# define DEF_MEM_LEVEL 8 +#else +# define DEF_MEM_LEVEL MAX_MEM_LEVEL +#endif +/* default memLevel */ + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +/* The three kinds of block type */ + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +/* The minimum and maximum match lengths */ + +#define PRESET_DICT 0x20 /* preset dictionary flag in zlib header */ + + /* target dependencies */ + +#if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) +# define OS_CODE 0x00 +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if(__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include <alloc.h> +# endif +# else /* MSC or DJGPP */ +# include <malloc.h> +# endif +#endif + +#ifdef AMIGA +# define OS_CODE 0x01 +#endif + +#if defined(VAXC) || defined(VMS) +# define OS_CODE 0x02 +# define F_OPEN(name, mode) \ + fopen((name), (mode), "mbc=60", "ctx=stm", "rfm=fix", "mrs=512") +#endif + +#if defined(ATARI) || defined(atarist) +# define OS_CODE 0x05 +#endif + +#ifdef OS2 +# define OS_CODE 0x06 +# ifdef M_I86 + #include <malloc.h> +# endif +#endif + +#if defined(MACOS) || defined(TARGET_OS_MAC) +# define OS_CODE 0x07 +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include <unix.h> /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif +# endif +#endif + +#ifdef TOPS20 +# define OS_CODE 0x0a +#endif + +#ifdef WIN32 +# ifndef __CYGWIN__ /* Cygwin is Unix, not Win32 */ +# define OS_CODE 0x0b +# endif +#endif + +#ifdef __50SERIES /* Prime/PRIMOS */ +# define OS_CODE 0x0f +#endif + +#if defined(_BEOS_) || defined(RISCOS) +# define fdopen(fd,mode) NULL /* No fdopen() */ +#endif + +#if (defined(_MSC_VER) && (_MSC_VER > 600)) +# if defined(_WIN32_WCE) +# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef _PTRDIFF_T_DEFINED + typedef int ptrdiff_t; +# define _PTRDIFF_T_DEFINED +# endif +# else +# define fdopen(fd,type) _fdopen(fd,type) +# endif +#endif + + /* common defaults */ + +#ifndef OS_CODE +# define OS_CODE 0x03 /* assume Unix */ +#endif + +#ifndef F_OPEN +# define F_OPEN(name, mode) fopen((name), (mode)) +#endif + + /* functions */ + +#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#if defined(__CYGWIN__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF +# endif +#endif +#ifndef HAVE_VSNPRINTF +# ifdef MSDOS + /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), + but for now we just assume it doesn't. */ +# define NO_vsnprintf +# endif +# ifdef __TURBOC__ +# define NO_vsnprintf +# endif +# ifdef WIN32 + /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ +# if !defined(vsnprintf) && !defined(NO_vsnprintf) +# define vsnprintf _vsnprintf +# endif +# endif +# ifdef __SASC +# define NO_vsnprintf +# endif +#endif +#ifdef VMS +# define NO_vsnprintf +#endif + +#if defined(pyr) +# define NO_MEMCPY +#endif +#if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) + /* Use our own functions for small and medium model with MSC <= 5.0. + * You may have to use the same strategy for Borland C (untested). + * The __SC__ check is for Symantec. + */ +# define NO_MEMCPY +#endif +#if defined(STDC) && !defined(HAVE_MEMCPY) && !defined(NO_MEMCPY) +# define HAVE_MEMCPY +#endif +#ifdef HAVE_MEMCPY +# ifdef SMALL_MEDIUM /* MSDOS small or medium model */ +# define zmemcpy _fmemcpy +# define zmemcmp _fmemcmp +# define zmemzero(dest, len) _fmemset(dest, 0, len) +# else +# define zmemcpy memcpy +# define zmemcmp memcmp +# define zmemzero(dest, len) memset(dest, 0, len) +# endif +#else + extern void zmemcpy OF((Bytef* dest, const Bytef* source, uInt len)); + extern int zmemcmp OF((const Bytef* s1, const Bytef* s2, uInt len)); + extern void zmemzero OF((Bytef* dest, uInt len)); +#endif + +/* Diagnostic functions */ +#ifdef DEBUG +# include <stdio.h> + extern int z_verbose; + extern void z_error OF((char *m)); +# define Assert(cond,msg) {if(!(cond)) z_error(msg);} +# define Trace(x) {if (z_verbose>=0) fprintf x ;} +# define Tracev(x) {if (z_verbose>0) fprintf x ;} +# define Tracevv(x) {if (z_verbose>1) fprintf x ;} +# define Tracec(c,x) {if (z_verbose>0 && (c)) fprintf x ;} +# define Tracecv(c,x) {if (z_verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + + +voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); +void zcfree OF((voidpf opaque, voidpf ptr)); + +#define ZALLOC(strm, items, size) \ + (*((strm)->zalloc))((strm)->opaque, (items), (size)) +#define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) +#define TRY_FREE(s, p) {if (p) ZFREE(s, p);} + +#endif /* ZUTIL_H */ |