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-2021 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 static void* ft_bzip2_alloc(FT_Memory memory,int items,int size)64 ft_bzip2_alloc( FT_Memory memory, 65 int items, 66 int size ) 67 { 68 FT_ULong sz = (FT_ULong)size * (FT_ULong)items; 69 FT_Error error; 70 FT_Pointer p = NULL; 71 72 73 FT_MEM_QALLOC( p, sz ); 74 return p; 75 } 76 77 78 static void ft_bzip2_free(FT_Memory memory,void * address)79 ft_bzip2_free( FT_Memory memory, 80 void* address ) 81 { 82 FT_MEM_FREE( address ); 83 } 84 85 86 /***************************************************************************/ 87 /***************************************************************************/ 88 /***** *****/ 89 /***** B Z I P 2 F I L E D E S C R I P T O R *****/ 90 /***** *****/ 91 /***************************************************************************/ 92 /***************************************************************************/ 93 94 #define FT_BZIP2_BUFFER_SIZE 4096 95 96 typedef struct FT_BZip2FileRec_ 97 { 98 FT_Stream source; /* parent/source stream */ 99 FT_Stream stream; /* embedding stream */ 100 FT_Memory memory; /* memory allocator */ 101 bz_stream bzstream; /* bzlib input stream */ 102 103 FT_Byte input[FT_BZIP2_BUFFER_SIZE]; /* input read buffer */ 104 105 FT_Byte buffer[FT_BZIP2_BUFFER_SIZE]; /* output buffer */ 106 FT_ULong pos; /* position in output */ 107 FT_Byte* cursor; 108 FT_Byte* limit; 109 110 } FT_BZip2FileRec, *FT_BZip2File; 111 112 113 /* check and skip .bz2 header - we don't support `transparent' compression */ 114 static FT_Error ft_bzip2_check_header(FT_Stream stream)115 ft_bzip2_check_header( FT_Stream stream ) 116 { 117 FT_Error error = FT_Err_Ok; 118 FT_Byte head[4]; 119 120 121 if ( FT_STREAM_SEEK( 0 ) || 122 FT_STREAM_READ( head, 4 ) ) 123 goto Exit; 124 125 /* head[0] && head[1] are the magic numbers; */ 126 /* head[2] is the version, and head[3] the blocksize */ 127 if ( head[0] != 0x42 || 128 head[1] != 0x5A || 129 head[2] != 0x68 ) /* only support bzip2 (huffman) */ 130 { 131 error = FT_THROW( Invalid_File_Format ); 132 goto Exit; 133 } 134 135 Exit: 136 return error; 137 } 138 139 140 static FT_Error ft_bzip2_file_init(FT_BZip2File zip,FT_Stream stream,FT_Stream source)141 ft_bzip2_file_init( FT_BZip2File zip, 142 FT_Stream stream, 143 FT_Stream source ) 144 { 145 bz_stream* bzstream = &zip->bzstream; 146 FT_Error error = FT_Err_Ok; 147 148 149 zip->stream = stream; 150 zip->source = source; 151 zip->memory = stream->memory; 152 153 zip->limit = zip->buffer + FT_BZIP2_BUFFER_SIZE; 154 zip->cursor = zip->limit; 155 zip->pos = 0; 156 157 /* check .bz2 header */ 158 { 159 stream = source; 160 161 error = ft_bzip2_check_header( stream ); 162 if ( error ) 163 goto Exit; 164 165 if ( FT_STREAM_SEEK( 0 ) ) 166 goto Exit; 167 } 168 169 /* initialize bzlib */ 170 bzstream->bzalloc = (alloc_func)ft_bzip2_alloc; 171 bzstream->bzfree = (free_func) ft_bzip2_free; 172 bzstream->opaque = zip->memory; 173 174 bzstream->avail_in = 0; 175 bzstream->next_in = (char*)zip->buffer; 176 177 if ( BZ2_bzDecompressInit( bzstream, 0, 0 ) != BZ_OK || 178 !bzstream->next_in ) 179 error = FT_THROW( Invalid_File_Format ); 180 181 Exit: 182 return error; 183 } 184 185 186 static void ft_bzip2_file_done(FT_BZip2File zip)187 ft_bzip2_file_done( FT_BZip2File zip ) 188 { 189 bz_stream* bzstream = &zip->bzstream; 190 191 192 BZ2_bzDecompressEnd( bzstream ); 193 194 /* clear the rest */ 195 bzstream->bzalloc = NULL; 196 bzstream->bzfree = NULL; 197 bzstream->opaque = NULL; 198 bzstream->next_in = NULL; 199 bzstream->next_out = NULL; 200 bzstream->avail_in = 0; 201 bzstream->avail_out = 0; 202 203 zip->memory = NULL; 204 zip->source = NULL; 205 zip->stream = NULL; 206 } 207 208 209 static FT_Error ft_bzip2_file_reset(FT_BZip2File zip)210 ft_bzip2_file_reset( FT_BZip2File zip ) 211 { 212 FT_Stream stream = zip->source; 213 FT_Error error; 214 215 216 if ( !FT_STREAM_SEEK( 0 ) ) 217 { 218 bz_stream* bzstream = &zip->bzstream; 219 220 221 BZ2_bzDecompressEnd( bzstream ); 222 223 bzstream->avail_in = 0; 224 bzstream->next_in = (char*)zip->input; 225 bzstream->avail_out = 0; 226 bzstream->next_out = (char*)zip->buffer; 227 228 zip->limit = zip->buffer + FT_BZIP2_BUFFER_SIZE; 229 zip->cursor = zip->limit; 230 zip->pos = 0; 231 232 BZ2_bzDecompressInit( bzstream, 0, 0 ); 233 } 234 235 return error; 236 } 237 238 239 static FT_Error ft_bzip2_file_fill_input(FT_BZip2File zip)240 ft_bzip2_file_fill_input( FT_BZip2File zip ) 241 { 242 bz_stream* bzstream = &zip->bzstream; 243 FT_Stream stream = zip->source; 244 FT_ULong size; 245 246 247 if ( stream->read ) 248 { 249 size = stream->read( stream, stream->pos, zip->input, 250 FT_BZIP2_BUFFER_SIZE ); 251 if ( size == 0 ) 252 { 253 zip->limit = zip->cursor; 254 return FT_THROW( Invalid_Stream_Operation ); 255 } 256 } 257 else 258 { 259 size = stream->size - stream->pos; 260 if ( size > FT_BZIP2_BUFFER_SIZE ) 261 size = FT_BZIP2_BUFFER_SIZE; 262 263 if ( size == 0 ) 264 { 265 zip->limit = zip->cursor; 266 return FT_THROW( Invalid_Stream_Operation ); 267 } 268 269 FT_MEM_COPY( zip->input, stream->base + stream->pos, size ); 270 } 271 stream->pos += size; 272 273 bzstream->next_in = (char*)zip->input; 274 bzstream->avail_in = size; 275 276 return FT_Err_Ok; 277 } 278 279 280 static FT_Error ft_bzip2_file_fill_output(FT_BZip2File zip)281 ft_bzip2_file_fill_output( FT_BZip2File zip ) 282 { 283 bz_stream* bzstream = &zip->bzstream; 284 FT_Error error = FT_Err_Ok; 285 286 287 zip->cursor = zip->buffer; 288 bzstream->next_out = (char*)zip->cursor; 289 bzstream->avail_out = FT_BZIP2_BUFFER_SIZE; 290 291 while ( bzstream->avail_out > 0 ) 292 { 293 int err; 294 295 296 if ( bzstream->avail_in == 0 ) 297 { 298 error = ft_bzip2_file_fill_input( zip ); 299 if ( error ) 300 break; 301 } 302 303 err = BZ2_bzDecompress( bzstream ); 304 305 if ( err == BZ_STREAM_END ) 306 { 307 zip->limit = (FT_Byte*)bzstream->next_out; 308 if ( zip->limit == zip->cursor ) 309 error = FT_THROW( Invalid_Stream_Operation ); 310 break; 311 } 312 else if ( err != BZ_OK ) 313 { 314 zip->limit = zip->cursor; 315 error = FT_THROW( Invalid_Stream_Operation ); 316 break; 317 } 318 } 319 320 return error; 321 } 322 323 324 /* fill output buffer; `count' must be <= FT_BZIP2_BUFFER_SIZE */ 325 static FT_Error ft_bzip2_file_skip_output(FT_BZip2File zip,FT_ULong count)326 ft_bzip2_file_skip_output( FT_BZip2File zip, 327 FT_ULong count ) 328 { 329 FT_Error error = FT_Err_Ok; 330 331 332 for (;;) 333 { 334 FT_ULong delta = (FT_ULong)( zip->limit - zip->cursor ); 335 336 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 = NULL; 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