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