1 /**************************************************************************** 2 * 3 * ftbzip2.c 4 * 5 * FreeType support for .bz2 compressed files. 6 * 7 * This optional component relies on libbz2. It should mainly be used to 8 * parse compressed PCF fonts, as found with many X11 server 9 * distributions. 10 * 11 * Copyright 2010-2018 by 12 * Joel Klinghed. 13 * 14 * based on `src/gzip/ftgzip.c' 15 * 16 * This file is part of the FreeType project, and may only be used, 17 * modified, and distributed under the terms of the FreeType project 18 * license, LICENSE.TXT. By continuing to use, modify, or distribute 19 * this file you indicate that you have read the license and 20 * understand and accept it fully. 21 * 22 */ 23 24 25 #include <ft2build.h> 26 #include FT_INTERNAL_MEMORY_H 27 #include FT_INTERNAL_STREAM_H 28 #include FT_INTERNAL_DEBUG_H 29 #include FT_BZIP2_H 30 #include FT_CONFIG_STANDARD_LIBRARY_H 31 32 33 #include FT_MODULE_ERRORS_H 34 35 #undef FTERRORS_H_ 36 37 #undef FT_ERR_PREFIX 38 #define FT_ERR_PREFIX Bzip2_Err_ 39 #define FT_ERR_BASE FT_Mod_Err_Bzip2 40 41 #include FT_ERRORS_H 42 43 44 #ifdef FT_CONFIG_OPTION_USE_BZIP2 45 46 #define BZ_NO_STDIO /* Do not need FILE */ 47 #include <bzlib.h> 48 49 50 /***************************************************************************/ 51 /***************************************************************************/ 52 /***** *****/ 53 /***** B Z I P 2 M E M O R Y M A N A G E M E N T *****/ 54 /***** *****/ 55 /***************************************************************************/ 56 /***************************************************************************/ 57 58 /* it is better to use FreeType memory routines instead of raw 59 'malloc/free' */ 60 61 typedef void *(* alloc_func)(void*, int, int); 62 typedef void (* free_func)(void*, void*); 63 64 static void* ft_bzip2_alloc(FT_Memory memory,int items,int size)65 ft_bzip2_alloc( FT_Memory memory, 66 int items, 67 int size ) 68 { 69 FT_ULong sz = (FT_ULong)size * (FT_ULong)items; 70 FT_Error error; 71 FT_Pointer p = NULL; 72 73 74 (void)FT_ALLOC( p, sz ); 75 return p; 76 } 77 78 79 static void ft_bzip2_free(FT_Memory memory,void * address)80 ft_bzip2_free( FT_Memory memory, 81 void* address ) 82 { 83 FT_MEM_FREE( address ); 84 } 85 86 87 /***************************************************************************/ 88 /***************************************************************************/ 89 /***** *****/ 90 /***** B Z I P 2 F I L E D E S C R I P T O R *****/ 91 /***** *****/ 92 /***************************************************************************/ 93 /***************************************************************************/ 94 95 #define FT_BZIP2_BUFFER_SIZE 4096 96 97 typedef struct FT_BZip2FileRec_ 98 { 99 FT_Stream source; /* parent/source stream */ 100 FT_Stream stream; /* embedding stream */ 101 FT_Memory memory; /* memory allocator */ 102 bz_stream bzstream; /* bzlib input stream */ 103 104 FT_Byte input[FT_BZIP2_BUFFER_SIZE]; /* input read buffer */ 105 106 FT_Byte buffer[FT_BZIP2_BUFFER_SIZE]; /* output buffer */ 107 FT_ULong pos; /* position in output */ 108 FT_Byte* cursor; 109 FT_Byte* limit; 110 111 } FT_BZip2FileRec, *FT_BZip2File; 112 113 114 /* check and skip .bz2 header - we don't support `transparent' compression */ 115 static FT_Error ft_bzip2_check_header(FT_Stream stream)116 ft_bzip2_check_header( FT_Stream stream ) 117 { 118 FT_Error error = FT_Err_Ok; 119 FT_Byte head[4]; 120 121 122 if ( FT_STREAM_SEEK( 0 ) || 123 FT_STREAM_READ( head, 4 ) ) 124 goto Exit; 125 126 /* head[0] && head[1] are the magic numbers; */ 127 /* head[2] is the version, and head[3] the blocksize */ 128 if ( head[0] != 0x42 || 129 head[1] != 0x5A || 130 head[2] != 0x68 ) /* only support bzip2 (huffman) */ 131 { 132 error = FT_THROW( Invalid_File_Format ); 133 goto Exit; 134 } 135 136 Exit: 137 return error; 138 } 139 140 141 static FT_Error ft_bzip2_file_init(FT_BZip2File zip,FT_Stream stream,FT_Stream source)142 ft_bzip2_file_init( FT_BZip2File zip, 143 FT_Stream stream, 144 FT_Stream source ) 145 { 146 bz_stream* bzstream = &zip->bzstream; 147 FT_Error error = FT_Err_Ok; 148 149 150 zip->stream = stream; 151 zip->source = source; 152 zip->memory = stream->memory; 153 154 zip->limit = zip->buffer + FT_BZIP2_BUFFER_SIZE; 155 zip->cursor = zip->limit; 156 zip->pos = 0; 157 158 /* check .bz2 header */ 159 { 160 stream = source; 161 162 error = ft_bzip2_check_header( stream ); 163 if ( error ) 164 goto Exit; 165 166 if ( FT_STREAM_SEEK( 0 ) ) 167 goto Exit; 168 } 169 170 /* initialize bzlib */ 171 bzstream->bzalloc = (alloc_func)ft_bzip2_alloc; 172 bzstream->bzfree = (free_func) ft_bzip2_free; 173 bzstream->opaque = zip->memory; 174 175 bzstream->avail_in = 0; 176 bzstream->next_in = (char*)zip->buffer; 177 178 if ( BZ2_bzDecompressInit( bzstream, 0, 0 ) != BZ_OK || 179 !bzstream->next_in ) 180 error = FT_THROW( Invalid_File_Format ); 181 182 Exit: 183 return error; 184 } 185 186 187 static void ft_bzip2_file_done(FT_BZip2File zip)188 ft_bzip2_file_done( FT_BZip2File zip ) 189 { 190 bz_stream* bzstream = &zip->bzstream; 191 192 193 BZ2_bzDecompressEnd( bzstream ); 194 195 /* clear the rest */ 196 bzstream->bzalloc = NULL; 197 bzstream->bzfree = NULL; 198 bzstream->opaque = NULL; 199 bzstream->next_in = NULL; 200 bzstream->next_out = NULL; 201 bzstream->avail_in = 0; 202 bzstream->avail_out = 0; 203 204 zip->memory = NULL; 205 zip->source = NULL; 206 zip->stream = NULL; 207 } 208 209 210 static FT_Error ft_bzip2_file_reset(FT_BZip2File zip)211 ft_bzip2_file_reset( FT_BZip2File zip ) 212 { 213 FT_Stream stream = zip->source; 214 FT_Error error; 215 216 217 if ( !FT_STREAM_SEEK( 0 ) ) 218 { 219 bz_stream* bzstream = &zip->bzstream; 220 221 222 BZ2_bzDecompressEnd( bzstream ); 223 224 bzstream->avail_in = 0; 225 bzstream->next_in = (char*)zip->input; 226 bzstream->avail_out = 0; 227 bzstream->next_out = (char*)zip->buffer; 228 229 zip->limit = zip->buffer + FT_BZIP2_BUFFER_SIZE; 230 zip->cursor = zip->limit; 231 zip->pos = 0; 232 233 BZ2_bzDecompressInit( bzstream, 0, 0 ); 234 } 235 236 return error; 237 } 238 239 240 static FT_Error ft_bzip2_file_fill_input(FT_BZip2File zip)241 ft_bzip2_file_fill_input( FT_BZip2File zip ) 242 { 243 bz_stream* bzstream = &zip->bzstream; 244 FT_Stream stream = zip->source; 245 FT_ULong size; 246 247 248 if ( stream->read ) 249 { 250 size = stream->read( stream, stream->pos, zip->input, 251 FT_BZIP2_BUFFER_SIZE ); 252 if ( size == 0 ) 253 { 254 zip->limit = zip->cursor; 255 return FT_THROW( Invalid_Stream_Operation ); 256 } 257 } 258 else 259 { 260 size = stream->size - stream->pos; 261 if ( size > FT_BZIP2_BUFFER_SIZE ) 262 size = FT_BZIP2_BUFFER_SIZE; 263 264 if ( size == 0 ) 265 { 266 zip->limit = zip->cursor; 267 return FT_THROW( Invalid_Stream_Operation ); 268 } 269 270 FT_MEM_COPY( zip->input, stream->base + stream->pos, size ); 271 } 272 stream->pos += size; 273 274 bzstream->next_in = (char*)zip->input; 275 bzstream->avail_in = size; 276 277 return FT_Err_Ok; 278 } 279 280 281 static FT_Error ft_bzip2_file_fill_output(FT_BZip2File zip)282 ft_bzip2_file_fill_output( FT_BZip2File zip ) 283 { 284 bz_stream* bzstream = &zip->bzstream; 285 FT_Error error = FT_Err_Ok; 286 287 288 zip->cursor = zip->buffer; 289 bzstream->next_out = (char*)zip->cursor; 290 bzstream->avail_out = FT_BZIP2_BUFFER_SIZE; 291 292 while ( bzstream->avail_out > 0 ) 293 { 294 int err; 295 296 297 if ( bzstream->avail_in == 0 ) 298 { 299 error = ft_bzip2_file_fill_input( zip ); 300 if ( error ) 301 break; 302 } 303 304 err = BZ2_bzDecompress( bzstream ); 305 306 if ( err == BZ_STREAM_END ) 307 { 308 zip->limit = (FT_Byte*)bzstream->next_out; 309 if ( zip->limit == zip->cursor ) 310 error = FT_THROW( Invalid_Stream_Operation ); 311 break; 312 } 313 else if ( err != BZ_OK ) 314 { 315 zip->limit = zip->cursor; 316 error = FT_THROW( Invalid_Stream_Operation ); 317 break; 318 } 319 } 320 321 return error; 322 } 323 324 325 /* fill output buffer; `count' must be <= FT_BZIP2_BUFFER_SIZE */ 326 static FT_Error ft_bzip2_file_skip_output(FT_BZip2File zip,FT_ULong count)327 ft_bzip2_file_skip_output( FT_BZip2File zip, 328 FT_ULong count ) 329 { 330 FT_Error error = FT_Err_Ok; 331 FT_ULong delta; 332 333 334 for (;;) 335 { 336 delta = (FT_ULong)( zip->limit - zip->cursor ); 337 if ( delta >= count ) 338 delta = count; 339 340 zip->cursor += delta; 341 zip->pos += delta; 342 343 count -= delta; 344 if ( count == 0 ) 345 break; 346 347 error = ft_bzip2_file_fill_output( zip ); 348 if ( error ) 349 break; 350 } 351 352 return error; 353 } 354 355 356 static FT_ULong ft_bzip2_file_io(FT_BZip2File zip,FT_ULong pos,FT_Byte * buffer,FT_ULong count)357 ft_bzip2_file_io( FT_BZip2File zip, 358 FT_ULong pos, 359 FT_Byte* buffer, 360 FT_ULong count ) 361 { 362 FT_ULong result = 0; 363 FT_Error error; 364 365 366 /* Reset inflate stream if we're seeking backwards. */ 367 /* Yes, that is not too efficient, but it saves memory :-) */ 368 if ( pos < zip->pos ) 369 { 370 error = ft_bzip2_file_reset( zip ); 371 if ( error ) 372 goto Exit; 373 } 374 375 /* skip unwanted bytes */ 376 if ( pos > zip->pos ) 377 { 378 error = ft_bzip2_file_skip_output( zip, (FT_ULong)( pos - zip->pos ) ); 379 if ( error ) 380 goto Exit; 381 } 382 383 if ( count == 0 ) 384 goto Exit; 385 386 /* now read the data */ 387 for (;;) 388 { 389 FT_ULong delta; 390 391 392 delta = (FT_ULong)( zip->limit - zip->cursor ); 393 if ( delta >= count ) 394 delta = count; 395 396 FT_MEM_COPY( buffer, zip->cursor, delta ); 397 buffer += delta; 398 result += delta; 399 zip->cursor += delta; 400 zip->pos += delta; 401 402 count -= delta; 403 if ( count == 0 ) 404 break; 405 406 error = ft_bzip2_file_fill_output( zip ); 407 if ( error ) 408 break; 409 } 410 411 Exit: 412 return result; 413 } 414 415 416 /***************************************************************************/ 417 /***************************************************************************/ 418 /***** *****/ 419 /***** B Z E M B E D D I N G S T R E A M *****/ 420 /***** *****/ 421 /***************************************************************************/ 422 /***************************************************************************/ 423 424 static void ft_bzip2_stream_close(FT_Stream stream)425 ft_bzip2_stream_close( FT_Stream stream ) 426 { 427 FT_BZip2File zip = (FT_BZip2File)stream->descriptor.pointer; 428 FT_Memory memory = stream->memory; 429 430 431 if ( zip ) 432 { 433 /* finalize bzip file descriptor */ 434 ft_bzip2_file_done( zip ); 435 436 FT_FREE( zip ); 437 438 stream->descriptor.pointer = NULL; 439 } 440 } 441 442 443 static unsigned long ft_bzip2_stream_io(FT_Stream stream,unsigned long offset,unsigned char * buffer,unsigned long count)444 ft_bzip2_stream_io( FT_Stream stream, 445 unsigned long offset, 446 unsigned char* buffer, 447 unsigned long count ) 448 { 449 FT_BZip2File zip = (FT_BZip2File)stream->descriptor.pointer; 450 451 452 return ft_bzip2_file_io( zip, offset, buffer, count ); 453 } 454 455 456 FT_EXPORT_DEF( FT_Error ) FT_Stream_OpenBzip2(FT_Stream stream,FT_Stream source)457 FT_Stream_OpenBzip2( FT_Stream stream, 458 FT_Stream source ) 459 { 460 FT_Error error; 461 FT_Memory memory; 462 FT_BZip2File zip = NULL; 463 464 465 if ( !stream || !source ) 466 { 467 error = FT_THROW( Invalid_Stream_Handle ); 468 goto Exit; 469 } 470 471 memory = source->memory; 472 473 /* 474 * check the header right now; this prevents allocating unnecessary 475 * objects when we don't need them 476 */ 477 error = ft_bzip2_check_header( source ); 478 if ( error ) 479 goto Exit; 480 481 FT_ZERO( stream ); 482 stream->memory = memory; 483 484 if ( !FT_QNEW( zip ) ) 485 { 486 error = ft_bzip2_file_init( zip, stream, source ); 487 if ( error ) 488 { 489 FT_FREE( zip ); 490 goto Exit; 491 } 492 493 stream->descriptor.pointer = zip; 494 } 495 496 stream->size = 0x7FFFFFFFL; /* don't know the real size! */ 497 stream->pos = 0; 498 stream->base = 0; 499 stream->read = ft_bzip2_stream_io; 500 stream->close = ft_bzip2_stream_close; 501 502 Exit: 503 return error; 504 } 505 506 #else /* !FT_CONFIG_OPTION_USE_BZIP2 */ 507 508 FT_EXPORT_DEF( FT_Error ) FT_Stream_OpenBzip2(FT_Stream stream,FT_Stream source)509 FT_Stream_OpenBzip2( FT_Stream stream, 510 FT_Stream source ) 511 { 512 FT_UNUSED( stream ); 513 FT_UNUSED( source ); 514 515 return FT_THROW( Unimplemented_Feature ); 516 } 517 518 #endif /* !FT_CONFIG_OPTION_USE_BZIP2 */ 519 520 521 /* END */ 522