~ubuntu-branches/ubuntu/raring/fyre/raring

« back to all changes in this revision

Viewing changes to src/chunked-file.c

  • Committer: Bazaar Package Importer
  • Author(s): Christoph Haas
  • Date: 2005-05-25 21:59:19 UTC
  • Revision ID: james.westby@ubuntu.com-20050525215919-jawtso5ic23qb401
Tags: upstream-1.0.0
ImportĀ upstreamĀ versionĀ 1.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- mode: c; c-basic-offset: 4; -*-
 
2
 *
 
3
 * chunked-file.c - A simple interface for reading and writing binary files
 
4
 *                  consisting of PNG-style chunks with 4-character identifiers.
 
5
 *
 
6
 *                  As with PNG images, the file consists of a fixed signature
 
7
 *                  followed by any number of chunks. Each chunk consists of
 
8
 *                  a 32-bit length, 4-byte chunk type, data, and a CRC.
 
9
 *                  The chunk format and CRC used here is compatible with PNG, but
 
10
 *                  this module does not specify the format of the chunk type
 
11
 *                  codes or of the file signature.
 
12
 *
 
13
 * Fyre - rendering and interactive exploration of chaotic functions
 
14
 * Copyright (C) 2004-2005 David Trowbridge and Micah Dowty
 
15
 *
 
16
 * This program is free software; you can redistribute it and/or
 
17
 * modify it under the terms of the GNU General Public License
 
18
 * as published by the Free Software Foundation; either version 2
 
19
 * of the License, or (at your option) any later version.
 
20
 *
 
21
 * This program is distributed in the hope that it will be useful,
 
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
24
 * GNU General Public License for more details.
 
25
 *
 
26
 * You should have received a copy of the GNU General Public License
 
27
 * along with this program; if not, write to the Free Software
 
28
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
29
 *
 
30
 */
 
31
 
 
32
#include <string.h>
 
33
#include "chunked-file.h"
 
34
 
 
35
/************************************************************************************/
 
36
/*************************************************************** CRC Implementation */
 
37
/************************************************************************************/
 
38
 
 
39
/* This implementation is a modified version of the one included in
 
40
 * the PNG specification's appendix. It has been altered to use GLib data types.
 
41
 */
 
42
 
 
43
/* Table of CRCs of all 8-bit messages. */
 
44
static guint32 crc_table[256];
 
45
 
 
46
/* Flag: has the table been computed? Initially false. */
 
47
static gboolean crc_table_computed = 0;
 
48
 
 
49
/* Make the table for a fast CRC. */
 
50
static void make_crc_table() {
 
51
    guint32 c;
 
52
    int n, k;
 
53
 
 
54
    for (n = 0; n < 256; n++) {
 
55
        c = (guint32) n;
 
56
        for (k = 0; k < 8; k++) {
 
57
            if (c & 1)
 
58
                c = 0xedb88320L ^ (c >> 1);
 
59
            else
 
60
                c = c >> 1;
 
61
        }
 
62
        crc_table[n] = c;
 
63
    }
 
64
    crc_table_computed = TRUE;
 
65
}
 
66
 
 
67
/* Update a running CRC with the bytes buf[0..len-1]--the CRC
 
68
 * should be initialized to all 1's, and the transmitted value
 
69
 * is the 1's complement of the final running CRC (see the
 
70
 * crc() routine below)).
 
71
 */
 
72
static guint32 update_crc(guint32 crc, const guchar *buf, guint len) {
 
73
    guint32 c = crc;
 
74
    guint n;
 
75
 
 
76
    if (!crc_table_computed)
 
77
        make_crc_table();
 
78
    for (n = 0; n < len; n++) {
 
79
        c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
 
80
    }
 
81
    return c;
 
82
}
 
83
 
 
84
/* Return the CRC of a data field and a type field */
 
85
static guint32 chunk_crc(ChunkType type, gsize length, const guchar* data) {
 
86
    guint32 c = 0xffffffffL;
 
87
    guint32 word;
 
88
 
 
89
    word = GUINT32_TO_BE(type);
 
90
    c = update_crc(c, (const guchar*) &word, sizeof(word));
 
91
 
 
92
    c = update_crc(c, data, length);
 
93
 
 
94
    return c ^ 0xffffffffL;
 
95
}
 
96
 
 
97
 
 
98
/************************************************************************************/
 
99
/******************************************************************* Public Methods */
 
100
/************************************************************************************/
 
101
 
 
102
void chunked_file_write_signature(FILE* self, const gchar* signature) {
 
103
    fwrite((void *) signature, strlen((void *) signature), 1, self);
 
104
}
 
105
 
 
106
gboolean chunked_file_read_signature(FILE* self, const gchar* signature) {
 
107
    /* Read a signature from the file, returning TRUE on success and FALSE on failure
 
108
     */
 
109
    int expected_size = strlen((void *) signature);
 
110
    gchar read_sig[expected_size];
 
111
 
 
112
    fseek(self, 0, SEEK_SET);
 
113
    if (fread(read_sig, 1, expected_size, self) != expected_size)
 
114
        return FALSE;
 
115
 
 
116
    if (memcmp(read_sig, (void *) signature, expected_size))
 
117
        return FALSE;
 
118
 
 
119
    return TRUE;
 
120
}
 
121
 
 
122
void chunked_file_write_chunk(FILE* self, ChunkType type, gsize length, const guchar* data) {
 
123
    guint32 word;
 
124
 
 
125
    word = GUINT32_TO_BE(length);
 
126
    fwrite(&word, sizeof(word), 1, self);
 
127
 
 
128
    word = GUINT32_TO_BE(type);
 
129
    fwrite(&word, sizeof(word), 1, self);
 
130
 
 
131
    fwrite(data, length, 1, self);
 
132
 
 
133
    word = GUINT32_TO_BE(chunk_crc(type, length, data));
 
134
    fwrite(&word, sizeof(word), 1, self);
 
135
}
 
136
 
 
137
gboolean chunked_file_read_chunk(FILE* self, ChunkType *type, gsize *length, guchar** data) {
 
138
    /* Try to read the next chunk from the file. Returns FALSE on EOF or
 
139
     * unrecoverable error. Returns TRUE on success, and sets type, length, and data
 
140
     * to hold the chunk contents. If this returns TRUE, the caller must free the
 
141
     * data buffer.
 
142
     */
 
143
    guint32 word;
 
144
 
 
145
    /* Loop until we get a valid chunk, we hit the end of file, or we hit an unrecoverable error */
 
146
    while (1) {
 
147
 
 
148
        /* Read length */
 
149
        if (fread(&word, 1, sizeof(word), self) != sizeof(word))
 
150
            return FALSE;
 
151
        *length = GUINT32_FROM_BE(word);
 
152
 
 
153
        /* Read type */
 
154
        if (fread(&word, 1, sizeof(word), self) != sizeof(word)) {
 
155
            g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
 
156
                  "Unexpected EOF trying to read chunk type");
 
157
            return FALSE;
 
158
        }
 
159
        *type = GUINT32_FROM_BE(word);
 
160
 
 
161
        /* Read data, allocating a new buffer for it */
 
162
        *data = g_malloc(*length);
 
163
        if (fread(*data, 1, *length, self) != *length) {
 
164
            gchar *type_string = chunk_type_to_string(*type);
 
165
            g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
 
166
                  "Unexpected EOF trying to read data for chunk of type %s", type_string);
 
167
            g_free(*data);
 
168
            g_free(type_string);
 
169
            return FALSE;
 
170
        }
 
171
 
 
172
        /* Read and validate the CRC. If it passes,
 
173
         * return successfully, otherwise issue a warning
 
174
         * and ignore the corrupted chunk.
 
175
         */
 
176
        if (fread(&word, 1, sizeof(word), self) != sizeof(word)) {
 
177
            g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
 
178
                  "Unexpected EOF trying to read chunk CRC");
 
179
            g_free(*data);
 
180
            return FALSE;
 
181
        }
 
182
        if (chunk_crc(*type, *length, *data) == GUINT32_FROM_BE(word)) {
 
183
            return TRUE;
 
184
        }
 
185
        else {
 
186
            gchar *type_string = chunk_type_to_string(*type);
 
187
            g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
 
188
                  "Ignoring corrupted chunk of type %s", type_string);
 
189
            g_free(*data);
 
190
            g_free(type_string);
 
191
        }
 
192
    }
 
193
}
 
194
 
 
195
void chunked_file_read_all(FILE* self, ChunkCallback callback, gpointer user_data) {
 
196
    ChunkType type;
 
197
    gsize length;
 
198
    guchar* data;
 
199
 
 
200
    while (chunked_file_read_chunk(self, &type, &length, &data)) {
 
201
        callback(user_data, type, length, data);
 
202
        g_free(data);
 
203
    }
 
204
}
 
205
 
 
206
void chunked_file_warn_unknown_type(ChunkType type) {
 
207
    gchar *type_string = chunk_type_to_string(type);
 
208
    g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING,
 
209
          "Ignoring unrecognized chunk of type %s", type_string);
 
210
    g_free(type_string);
 
211
}
 
212
 
 
213
gchar* chunk_type_to_string(ChunkType type) {
 
214
    return g_strdup_printf("'%c%c%c%c'",
 
215
                           (type >> 24) & 0xFF,
 
216
                           (type >> 16) & 0xFF,
 
217
                           (type >> 8) & 0xFF,
 
218
                           type & 0xFF);
 
219
}
 
220
 
 
221
/* The End */