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-2020 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 (void)FT_ALLOC( 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 FT_ULong delta; 331 332 333 for (;;) 334 { 335 delta = (FT_ULong)( zip->limit - zip->cursor ); 336 if ( delta >= count ) 337 delta = count; 338 339 zip->cursor += delta; 340 zip->pos += delta; 341 342 count -= delta; 343 if ( count == 0 ) 344 break; 345 346 error = ft_bzip2_file_fill_output( zip ); 347 if ( error ) 348 break; 349 } 350 351 return error; 352 } 353 354 355 static FT_ULong ft_bzip2_file_io(FT_BZip2File zip,FT_ULong pos,FT_Byte * buffer,FT_ULong count)356 ft_bzip2_file_io( FT_BZip2File zip, 357 FT_ULong pos, 358 FT_Byte* buffer, 359 FT_ULong count ) 360 { 361 FT_ULong result = 0; 362 FT_Error error; 363 364 365 /* Reset inflate stream if we're seeking backwards. */ 366 /* Yes, that is not too efficient, but it saves memory :-) */ 367 if ( pos < zip->pos ) 368 { 369 error = ft_bzip2_file_reset( zip ); 370 if ( error ) 371 goto Exit; 372 } 373 374 /* skip unwanted bytes */ 375 if ( pos > zip->pos ) 376 { 377 error = ft_bzip2_file_skip_output( zip, (FT_ULong)( pos - zip->pos ) ); 378 if ( error ) 379 goto Exit; 380 } 381 382 if ( count == 0 ) 383 goto Exit; 384 385 /* now read the data */ 386 for (;;) 387 { 388 FT_ULong delta; 389 390 391 delta = (FT_ULong)( zip->limit - zip->cursor ); 392 if ( delta >= count ) 393 delta = count; 394 395 FT_MEM_COPY( buffer, zip->cursor, delta ); 396 buffer += delta; 397 result += delta; 398 zip->cursor += delta; 399 zip->pos += delta; 400 401 count -= delta; 402 if ( count == 0 ) 403 break; 404 405 error = ft_bzip2_file_fill_output( zip ); 406 if ( error ) 407 break; 408 } 409 410 Exit: 411 return result; 412 } 413 414 415 /***************************************************************************/ 416 /***************************************************************************/ 417 /***** *****/ 418 /***** B Z E M B E D D I N G S T R E A M *****/ 419 /***** *****/ 420 /***************************************************************************/ 421 /***************************************************************************/ 422 423 static void ft_bzip2_stream_close(FT_Stream stream)424 ft_bzip2_stream_close( FT_Stream stream ) 425 { 426 FT_BZip2File zip = (FT_BZip2File)stream->descriptor.pointer; 427 FT_Memory memory = stream->memory; 428 429 430 if ( zip ) 431 { 432 /* finalize bzip file descriptor */ 433 ft_bzip2_file_done( zip ); 434 435 FT_FREE( zip ); 436 437 stream->descriptor.pointer = NULL; 438 } 439 } 440 441 442 static unsigned long ft_bzip2_stream_io(FT_Stream stream,unsigned long offset,unsigned char * buffer,unsigned long count)443 ft_bzip2_stream_io( FT_Stream stream, 444 unsigned long offset, 445 unsigned char* buffer, 446 unsigned long count ) 447 { 448 FT_BZip2File zip = (FT_BZip2File)stream->descriptor.pointer; 449 450 451 return ft_bzip2_file_io( zip, offset, buffer, count ); 452 } 453 454 455 FT_EXPORT_DEF( FT_Error ) FT_Stream_OpenBzip2(FT_Stream stream,FT_Stream source)456 FT_Stream_OpenBzip2( FT_Stream stream, 457 FT_Stream source ) 458 { 459 FT_Error error; 460 FT_Memory memory; 461 FT_BZip2File zip = NULL; 462 463 464 if ( !stream || !source ) 465 { 466 error = FT_THROW( Invalid_Stream_Handle ); 467 goto Exit; 468 } 469 470 memory = source->memory; 471 472 /* 473 * check the header right now; this prevents allocating unnecessary 474 * objects when we don't need them 475 */ 476 error = ft_bzip2_check_header( source ); 477 if ( error ) 478 goto Exit; 479 480 FT_ZERO( stream ); 481 stream->memory = memory; 482 483 if ( !FT_QNEW( zip ) ) 484 { 485 error = ft_bzip2_file_init( zip, stream, source ); 486 if ( error ) 487 { 488 FT_FREE( zip ); 489 goto Exit; 490 } 491 492 stream->descriptor.pointer = zip; 493 } 494 495 stream->size = 0x7FFFFFFFL; /* don't know the real size! */ 496 stream->pos = 0; 497 stream->base = 0; 498 stream->read = ft_bzip2_stream_io; 499 stream->close = ft_bzip2_stream_close; 500 501 Exit: 502 return error; 503 } 504 505 #else /* !FT_CONFIG_OPTION_USE_BZIP2 */ 506 507 FT_EXPORT_DEF( FT_Error ) FT_Stream_OpenBzip2(FT_Stream stream,FT_Stream source)508 FT_Stream_OpenBzip2( FT_Stream stream, 509 FT_Stream source ) 510 { 511 FT_UNUSED( stream ); 512 FT_UNUSED( source ); 513 514 return FT_THROW( Unimplemented_Feature ); 515 } 516 517 #endif /* !FT_CONFIG_OPTION_USE_BZIP2 */ 518 519 520 /* END */ 521