+#include <config.h>
#include "stream.h"
#include "fileref.h"
#include "magic.h"
+#include "gi_blorb.h"
#include <errno.h>
#include <stdio.h>
#include <glib.h>
#include <glib/gstdio.h>
+#include <glib/gi18n-lib.h>
#include "chimara-glk-private.h"
extern GPrivate *glk_data_key;
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;
}
/**
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;
}
/**
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;
+#include <config.h>
#include "charset.h"
#include "magic.h"
#include "stream.h"
#include <string.h>
#include <glib.h>
#include <glib/gstdio.h>
+#include <glib/gi18n-lib.h>
/* 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
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);
}
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);
}
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
{
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)
{
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;
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;
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;
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;
switch(str->type)
{
case STREAM_TYPE_MEMORY:
+ case STREAM_TYPE_RESOURCE:
return str->mark;
case STREAM_TYPE_FILE:
return ftell(str->file_pointer);
switch(str->type)
{
+ case STREAM_TYPE_RESOURCE:
case STREAM_TYPE_MEMORY:
switch(seekmode)
{