From 7cbc2c68d146433b1d438d60406ca05e14a523ce Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Sun, 12 Feb 2012 12:36:28 +0100 Subject: [PATCH] Implement resource streams --- libchimara/gestalt.c | 4 +- libchimara/stream.c | 75 +++++++++++++++++- libchimara/stream.h | 13 ++-- libchimara/strio.c | 178 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 259 insertions(+), 11 deletions(-) diff --git a/libchimara/gestalt.c b/libchimara/gestalt.c index 55ff57c..1dad9d9 100644 --- a/libchimara/gestalt.c +++ b/libchimara/gestalt.c @@ -123,6 +123,7 @@ glk_gestalt_ext(glui32 sel, glui32 val, glui32 *arr, glui32 arrlen) case gestalt_UnicodeNorm: case gestalt_LineInputEcho: case gestalt_LineTerminators: + case gestalt_ResourceStream: return 1; /* Capabilities supported if compiled with GStreamer */ @@ -138,8 +139,7 @@ glk_gestalt_ext(glui32 sel, glui32 val, glui32 *arr, glui32 arrlen) #endif /* Unsupported capabilities */ - case gestalt_ResourceStream: - return 0; + /* None! */ /* Selector not supported */ default: diff --git a/libchimara/stream.c b/libchimara/stream.c index f9a6cf5..0b1e2d8 100644 --- a/libchimara/stream.c +++ b/libchimara/stream.c @@ -1,10 +1,13 @@ +#include #include "stream.h" #include "fileref.h" #include "magic.h" +#include "gi_blorb.h" #include #include #include #include +#include #include "chimara-glk-private.h" extern GPrivate *glk_data_key; @@ -516,8 +519,58 @@ glk_stream_open_file_uni(frefid_t fileref, glui32 fmode, glui32 rock) strid_t glk_stream_open_resource(glui32 filenum, glui32 rock) { - g_warning("Not implemented"); - return NULL; + /* Adapted from CheapGlk */ + strid_t str; + gboolean isbinary; + giblorb_err_t err; + giblorb_result_t res; + giblorb_map_t *map = giblorb_get_resource_map(); + if(map == NULL) { + WARNING(_("Could not create resource stream, because there was no " + "resource map.")); + return NULL; /* Not running from a blorb file */ + } + + err = giblorb_load_resource(map, giblorb_method_Memory, &res, giblorb_ID_Data, filenum); + if(err) { + WARNING_S(_("Could not create resource stream, because the resource " + "could not be loaded"), giblorb_get_error_message(err)); + return 0; /* Not found, or some other error */ + } + + /* We'll use the in-memory copy of the chunk data as the basis for + our new stream. It's important to not call chunk_unload() until + the stream is closed (and we won't). + + This will be memory-hoggish for giant data chunks, but I don't + expect giant data chunks at this point. A more efficient model + would be to use the file on disk, but this requires some hacking + into the file stream code (we'd need to open a new FILE*) and + I don't feel like doing that. */ + + if(res.chunktype == giblorb_ID_TEXT) + isbinary = FALSE; + else if(res.chunktype == giblorb_ID_BINA) + isbinary = TRUE; + else { + WARNING(_("Could not create resource stream, because chunk was of " + "unknown type.")); + return NULL; /* Unknown chunk type */ + } + + str = stream_new_common(rock); + str->type = STREAM_TYPE_RESOURCE; + str->file_mode = filemode_Read; + str->binary = isbinary; + + if (res.data.ptr && res.length) { + str->buffer = res.data.ptr; + str->mark = 0; + str->buflen = res.length; + str->endmark = str->buflen; + } + + return str; } /** @@ -542,8 +595,16 @@ glk_stream_open_resource(glui32 filenum, glui32 rock) strid_t glk_stream_open_resource_uni(glui32 filenum, glui32 rock) { - g_warning("Not implemented"); - return NULL; + /* Adapted from CheapGlk */ + /* We have been handed an array of bytes. (They're big-endian + four-byte chunks, or perhaps a UTF-8 byte sequence, rather than + native-endian four-byte integers). So we drop it into str->buffer, + rather than str->ubuffer -- we'll have to do the translation in the + get() functions. */ + strid_t str = glk_stream_open_resource(filenum, rock); + if(str != NULL) + str->unicode = TRUE; + return str; } /** @@ -592,6 +653,12 @@ glk_stream_close(strid_t str, stream_result_t *result) IO_WARNING( "Failed to close file", str->filename, g_strerror(errno) ); g_free(str->filename); break; + + case STREAM_TYPE_RESOURCE: + /* Shouldn't free the chunk; someone else might be using it. It will + be freed when the resource map is freed. */ + break; + default: ILLEGAL_PARAM("Unknown stream type: %u", str->type); return; diff --git a/libchimara/stream.h b/libchimara/stream.h index 7ff7618..33497ce 100644 --- a/libchimara/stream.h +++ b/libchimara/stream.h @@ -12,7 +12,8 @@ enum StreamType { STREAM_TYPE_WINDOW, STREAM_TYPE_MEMORY, - STREAM_TYPE_FILE + STREAM_TYPE_FILE, + STREAM_TYPE_RESOURCE }; /** @@ -36,18 +37,20 @@ struct glk_stream_struct enum StreamType type; /* Specific to window stream: the window this stream is connected to */ winid_t window; - /* For memory and file streams */ + /* For memory, file, and resource streams */ gboolean unicode; - /* Specific to memory streams */ - gchar *buffer; + /* For file and resource streams */ + gboolean binary; + /* For memory and resource streams */ + char *buffer; glui32 *ubuffer; glui32 mark; glui32 endmark; glui32 buflen; + /* Specific to memory streams */ gidispatch_rock_t buffer_rock; /* Specific to file streams */ FILE *file_pointer; - gboolean binary; gchar *filename; /* Displayable filename in UTF-8 for error handling */ glui32 lastop; /* 0, filemode_Write, or filemode_Read */ diff --git a/libchimara/strio.c b/libchimara/strio.c index 41146f2..d8dda82 100644 --- a/libchimara/strio.c +++ b/libchimara/strio.c @@ -1,3 +1,4 @@ +#include #include "charset.h" #include "magic.h" #include "stream.h" @@ -6,6 +7,7 @@ #include #include #include +#include /* Internal function: ensure that an fseek() is called on a file pointer in between reading and writing operations, and vice versa. This will only come up @@ -346,6 +348,9 @@ write_buffer_to_stream(strid_t str, gchar *buf, glui32 len) str->write_count += len; break; + case STREAM_TYPE_RESOURCE: + ILLEGAL(_("Writing to a resource stream is illegal.")); + break; default: ILLEGAL_PARAM("Unknown stream type: %u", str->type); } @@ -444,6 +449,9 @@ write_buffer_to_stream_uni(strid_t str, glui32 *buf, glui32 len) str->write_count += len; break; + case STREAM_TYPE_RESOURCE: + ILLEGAL(_("Writing to a resource stream is illegal.")); + break; default: ILLEGAL_PARAM("Unknown stream type: %u", str->type); } @@ -623,6 +631,58 @@ read_utf8_char_from_file(strid_t str) return charresult; } +/* Internal function: Read one UTF-8 character, which may be more than one byte, +from a memory stream @str, and return it as a Unicode code point. */ +static glsi32 +read_utf8_char_from_buffer(strid_t str) +{ + size_t foo; + gunichar charresult = (gunichar)-2; + char *buffer = str->buffer + str->mark; + size_t maxlen = str->buflen - str->mark; + + if(maxlen == 0) + return -1; + + for(foo = 1; foo <= maxlen; foo++) + { + charresult = g_utf8_get_char_validated(buffer, foo); + /* charresult is -1 if invalid, -2 if incomplete, and the + Unicode code point otherwise */ + if(charresult != (gunichar)-2) + break; + } + str->mark += foo; + str->read_count++; + + /* Return -1 on EOS */ + if(charresult == (gunichar)-2) + return -1; + /* Silently return unknown characters as 0xFFFD, Replacement Character */ + if(charresult == (gunichar)-1) + return 0xFFFD; + return charresult; +} + +/* Internal function: Read one big-endian four-byte character from memory and +return it as a Unicode code point, or -1 on EOF */ +static glsi32 +read_ucs4be_char_from_buffer(strid_t str) +{ + glui32 ch = str->buffer[str->mark++]; + if(str->mark >= str->buflen) + return -1; + ch = (ch << 8) | (str->buffer[str->mark++] & 0xFF); + if(str->mark >= str->buflen) + return -1; + ch = (ch << 8) | (str->buffer[str->mark++] & 0xFF); + if(str->mark >= str->buflen) + return -1; + ch = (ch << 8) | (str->buffer[str->mark++] & 0xFF); + str->read_count++; + return ch; +} + /* Internal function: Tell whether this code point is a Unicode newline. The file pointer and eight-bit flag are included in case the newline is a CR (U+000D). If the next character is LF (U+000A) then it also belongs to the @@ -653,6 +713,18 @@ get_char_stream_common(strid_t str) { switch(str->type) { + case STREAM_TYPE_RESOURCE: + if(str->unicode) + { + if(!str->buffer || str->mark >= str->buflen) + return -1; + if(str->binary) + /* Cheap big-endian stream */ + return read_ucs4be_char_from_buffer(str); + /* slightly less cheap UTF8 stream */ + return read_utf8_char_from_buffer(str); + } + /* for text streams, fall through to memory case */ case STREAM_TYPE_MEMORY: if(str->unicode) { @@ -785,6 +857,24 @@ glk_get_buffer_stream(strid_t str, char *buf, glui32 len) switch(str->type) { + case STREAM_TYPE_RESOURCE: + { + int copycount = 0; + if(str->unicode) + { + while(copycount < len && str->buffer && str->mark < str->buflen) + { + glui32 ch; + if(str->binary) + ch = read_ucs4be_char_from_buffer(str); + else + ch = read_utf8_char_from_buffer(str); + buf[copycount++] = (ch > 0xFF)? '?' : (char)ch; + } + return copycount; + } + /* for text streams, fall through to memory case */ + } case STREAM_TYPE_MEMORY: { int copycount = 0; @@ -884,6 +974,24 @@ glk_get_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len) switch(str->type) { + case STREAM_TYPE_RESOURCE: + { + int copycount = 0; + if(str->unicode) + { + while(copycount < len && str->buffer && str->mark < str->buflen) + { + glui32 ch; + if(str->binary) + ch = read_ucs4be_char_from_buffer(str); + else + ch = read_utf8_char_from_buffer(str); + buf[copycount++] = ch; + } + return copycount; + } + /* for text streams, fall through to memory case */ + } case STREAM_TYPE_MEMORY: { int copycount = 0; @@ -992,6 +1100,40 @@ glk_get_line_stream(strid_t str, char *buf, glui32 len) switch(str->type) { + case STREAM_TYPE_RESOURCE: + { + int copycount = 0; + if(str->unicode) + { + /* Do it character-by-character */ + while(copycount < len - 1 && str->buffer && str->mark < str->buflen) + { + glsi32 ch; + if(str->binary) + ch = read_ucs4be_char_from_buffer(str); + else + ch = read_utf8_char_from_buffer(str); + /* Check for Unicode newline; slightly different than + in file streams */ + if(ch == 0x0A || ch == 0x85 || ch == 0x0C || ch == 0x2028 || ch == 0x2029) + { + buf[copycount++] = '\n'; + break; + } + if(ch == 0x0D) + { + if(str->buffer[str->mark] == 0x0A) + str->mark++; /* skip past next newline */ + buf[copycount++] = '\n'; + break; + } + buf[copycount++] = (ch > 0xFF)? '?' : (char)ch; + } + buf[copycount] = '\0'; + return copycount; + } + /* for text streams, fall through to the memory case */ + } case STREAM_TYPE_MEMORY: { int copycount = 0; @@ -1129,6 +1271,40 @@ glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len) switch(str->type) { + case STREAM_TYPE_RESOURCE: + { + int copycount = 0; + if(str->unicode) + { + /* Do it character-by-character */ + while(copycount < len - 1 && str->buffer && str->mark < str->buflen) + { + glsi32 ch; + if(str->binary) + ch = read_ucs4be_char_from_buffer(str); + else + ch = read_utf8_char_from_buffer(str); + /* Check for Unicode newline; slightly different than + in file streams */ + if(ch == 0x0A || ch == 0x85 || ch == 0x0C || ch == 0x2028 || ch == 0x2029) + { + buf[copycount++] = '\n'; + break; + } + if(ch == 0x0D) + { + if(str->ubuffer[str->mark] == 0x0A) + str->mark++; /* skip past next newline */ + buf[copycount++] = '\n'; + break; + } + buf[copycount++] = ch; + } + buf[copycount] = '\0'; + return copycount; + } + /* for text streams, fall through to the memory case */ + } case STREAM_TYPE_MEMORY: { int copycount = 0; @@ -1297,6 +1473,7 @@ glk_stream_get_position(strid_t str) switch(str->type) { case STREAM_TYPE_MEMORY: + case STREAM_TYPE_RESOURCE: return str->mark; case STREAM_TYPE_FILE: return ftell(str->file_pointer); @@ -1346,6 +1523,7 @@ glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekmode) switch(str->type) { + case STREAM_TYPE_RESOURCE: case STREAM_TYPE_MEMORY: switch(seekmode) { -- 2.30.2