1
/* -*- mode: c; c-basic-offset: 4; -*-
3
* chunked-file.c - A simple interface for reading and writing binary files
4
* consisting of PNG-style chunks with 4-character identifiers.
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.
13
* Fyre - rendering and interactive exploration of chaotic functions
14
* Copyright (C) 2004-2005 David Trowbridge and Micah Dowty
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.
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.
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.
33
#include "chunked-file.h"
35
/************************************************************************************/
36
/*************************************************************** CRC Implementation */
37
/************************************************************************************/
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.
43
/* Table of CRCs of all 8-bit messages. */
44
static guint32 crc_table[256];
46
/* Flag: has the table been computed? Initially false. */
47
static gboolean crc_table_computed = 0;
49
/* Make the table for a fast CRC. */
50
static void make_crc_table() {
54
for (n = 0; n < 256; n++) {
56
for (k = 0; k < 8; k++) {
58
c = 0xedb88320L ^ (c >> 1);
64
crc_table_computed = TRUE;
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)).
72
static guint32 update_crc(guint32 crc, const guchar *buf, guint len) {
76
if (!crc_table_computed)
78
for (n = 0; n < len; n++) {
79
c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
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;
89
word = GUINT32_TO_BE(type);
90
c = update_crc(c, (const guchar*) &word, sizeof(word));
92
c = update_crc(c, data, length);
94
return c ^ 0xffffffffL;
98
/************************************************************************************/
99
/******************************************************************* Public Methods */
100
/************************************************************************************/
102
void chunked_file_write_signature(FILE* self, const gchar* signature) {
103
fwrite((void *) signature, strlen((void *) signature), 1, self);
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
109
int expected_size = strlen((void *) signature);
110
gchar read_sig[expected_size];
112
fseek(self, 0, SEEK_SET);
113
if (fread(read_sig, 1, expected_size, self) != expected_size)
116
if (memcmp(read_sig, (void *) signature, expected_size))
122
void chunked_file_write_chunk(FILE* self, ChunkType type, gsize length, const guchar* data) {
125
word = GUINT32_TO_BE(length);
126
fwrite(&word, sizeof(word), 1, self);
128
word = GUINT32_TO_BE(type);
129
fwrite(&word, sizeof(word), 1, self);
131
fwrite(data, length, 1, self);
133
word = GUINT32_TO_BE(chunk_crc(type, length, data));
134
fwrite(&word, sizeof(word), 1, self);
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
145
/* Loop until we get a valid chunk, we hit the end of file, or we hit an unrecoverable error */
149
if (fread(&word, 1, sizeof(word), self) != sizeof(word))
151
*length = GUINT32_FROM_BE(word);
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");
159
*type = GUINT32_FROM_BE(word);
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);
172
/* Read and validate the CRC. If it passes,
173
* return successfully, otherwise issue a warning
174
* and ignore the corrupted chunk.
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");
182
if (chunk_crc(*type, *length, *data) == GUINT32_FROM_BE(word)) {
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);
195
void chunked_file_read_all(FILE* self, ChunkCallback callback, gpointer user_data) {
200
while (chunked_file_read_chunk(self, &type, &length, &data)) {
201
callback(user_data, type, length, data);
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);
213
gchar* chunk_type_to_string(ChunkType type) {
214
return g_strdup_printf("'%c%c%c%c'",