LCOV - code coverage report
Current view: directory - frmts/bsb - bsb_read.c (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 364 218 59.9 %
Date: 2010-01-09 Functions: 10 7 70.0 %

       1                 : /******************************************************************************
       2                 :  * $Id: bsb_read.c 17405 2009-07-17 06:13:24Z chaitanya $
       3                 :  *
       4                 :  * Project:  BSB Reader
       5                 :  * Purpose:  Low level BSB Access API Implementation (non-GDAL).
       6                 :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       7                 :  *
       8                 :  * NOTE: This code is implemented on the basis of work by Mike Higgins.  The 
       9                 :  * BSB format is subject to US patent 5,727,090; however, that patent 
      10                 :  * apparently only covers *writing* BSB files, not reading them, so this code 
      11                 :  * should not be affected.
      12                 :  *
      13                 :  ******************************************************************************
      14                 :  * Copyright (c) 2001, Frank Warmerdam
      15                 :  *
      16                 :  * Permission is hereby granted, free of charge, to any person obtaining a
      17                 :  * copy of this software and associated documentation files (the "Software"),
      18                 :  * to deal in the Software without restriction, including without limitation
      19                 :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      20                 :  * and/or sell copies of the Software, and to permit persons to whom the
      21                 :  * Software is furnished to do so, subject to the following conditions:
      22                 :  *
      23                 :  * The above copyright notice and this permission notice shall be included
      24                 :  * in all copies or substantial portions of the Software.
      25                 :  *
      26                 :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      27                 :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      28                 :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      29                 :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      30                 :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      31                 :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      32                 :  * DEALINGS IN THE SOFTWARE.
      33                 :  ****************************************************************************/
      34                 : 
      35                 : #include "bsb_read.h"
      36                 : #include "cpl_conv.h"
      37                 : #include "cpl_string.h"
      38                 : 
      39                 : CPL_CVSID("$Id: bsb_read.c 17405 2009-07-17 06:13:24Z chaitanya $");
      40                 : 
      41                 : static int BSBReadHeaderLine( BSBInfo *psInfo, char* pszLine, int nLineMaxLen, int bNO1 );
      42                 : static int BSBSeekAndCheckScanlineNumber ( BSBInfo *psInfo, int nScanline,
      43                 :                                            int bVerboseIfError );
      44                 : /************************************************************************
      45                 : 
      46                 : Background:
      47                 : 
      48                 : To: Frank Warmerdam <warmerda@home.com>
      49                 : From: Mike Higgins <higgins@monitor.net>
      50                 : Subject: Re: GISTrans: Maptech / NDI BSB Chart Format
      51                 : Mime-Version: 1.0
      52                 : Content-Type: text/plain; charset="us-ascii"; format=flowed
      53                 : 
      54                 :          I did it! I just wrote a program that reads NOAA BSB chart files 
      55                 : and converts them to BMP files! BMP files are not the final goal of my 
      56                 : project, but it served as a proof-of-concept.  Next I will want to write 
      57                 : routines to extract pieces of the file at full resolution for printing, and 
      58                 : routines to filter pieces of the chart for display at lower resolution on 
      59                 : the screen.  (One of the terrible things about most chart display programs 
      60                 : is that they all sub-sample the charts instead of filtering it down). How 
      61                 : did I figure out how to read the BSB files?
      62                 : 
      63                 :          If you recall, I have been trying to reverse engineer the file 
      64                 : formats of those nautical charts. When I am between projects I often do a 
      65                 : WEB search for the BSB file format to see if someone else has published a 
      66                 : hack for them. Monday I hit a NOAA project status report that mentioned 
      67                 : some guy named Marty Yellin who had recently completed writing a program to 
      68                 : convert BSB files to other file formats! I searched for him and found him 
      69                 : mentioned as a contact person for some NOAA program. I was composing a 
      70                 : letter to him in my head, or considering calling the NOAA phone number and 
      71                 : asking for his extension number, when I saw another NOAA status report 
      72                 : indicating that he had retired in 1998. His name showed up in a few more 
      73                 : reports, one of which said that he was the inventor of the BSB file format, 
      74                 : that it was patented (#5,727,090), and that the patent had been licensed to 
      75                 : Maptech (the evil company that will not allow anyone using their file 
      76                 : format to convert them to non-proprietary formats). Patents are readily 
      77                 : available on the WEB at the IBM patent server and this one is in the 
      78                 : dtabase!  I printed up a copy of the patent and of course it describes very 
      79                 : nicely (despite the usual typos and omissions of referenced items in the 
      80                 : figures) how to write one of these BSB files!
      81                 : 
      82                 :          I was considering talking to a patent lawyer about the legality of 
      83                 : using information in the patent to read files without getting a license,
      84                 : when I noticed that the patent is only claiming programs that WRITE the 
      85                 : file format. I have noticed this before in RF patents where they describe 
      86                 : how to make a receiver and never bother to claim a transmitter. The logic 
      87                 : is that the transmitter is no good to anybody unless they license receivers 
      88                 : from the patent holder. But I think they did it backwards here! They should 
      89                 : have claimed a program that can READ the described file format. Now I can 
      90                 : read the files, build programs that read the files, and even sell them 
      91                 : without violating the claims in the patent! As long as I never try to write 
      92                 : one of the evil BSB files, I'm OK!!!
      93                 : 
      94                 :          If you ever need to read these BSB chart programs, drop me a 
      95                 : note.  I would be happy to send you a copy of this conversion program.
      96                 : 
      97                 : ... later email ...
      98                 : 
      99                 :          Well, here is my little proof of concept program. I hereby give 
     100                 : you permission to distribute it freely, modify for you own use, etc.
     101                 : I built it as a "WIN32 Console application" which means it runs in an MS 
     102                 : DOS box under Microsoft Windows. But the only Windows specific stuff in it 
     103                 : are the include files for the BMP file headers.  If you ripped out the BMP 
     104                 : code it should compile under UNIX or anyplace else.
     105                 :          I'd be overjoyed to have you announce it to GISTrans or anywhere 
     106                 : else.  I'm philosophically opposed to the proprietary treatment of the  BSB 
     107                 : file format and I want to break it open! Chart data for the People!
     108                 : 
     109                 :  ************************************************************************/
     110                 : 
     111                 : /************************************************************************/
     112                 : /*                             BSBUngetc()                              */
     113                 : /************************************************************************/
     114                 : 
     115                 : static
     116            1335 : void BSBUngetc( BSBInfo *psInfo, int nCharacter )
     117                 : 
     118                 : {
     119                 :     CPLAssert( psInfo->nSavedCharacter == -1000 );
     120            1335 :     psInfo->nSavedCharacter = nCharacter;
     121            1335 : }
     122                 : 
     123                 : /************************************************************************/
     124                 : /*                              BSBGetc()                               */
     125                 : /************************************************************************/
     126                 : 
     127                 : static
     128           26244 : int BSBGetc( BSBInfo *psInfo, int bNO1, int* pbErrorFlag )
     129                 : 
     130                 : {
     131                 :     int nByte;
     132                 : 
     133           26244 :     if( psInfo->nSavedCharacter != -1000 )
     134                 :     {
     135            1335 :         nByte = psInfo->nSavedCharacter;
     136            1335 :         psInfo->nSavedCharacter = -1000;
     137            1335 :         return nByte;
     138                 :     }
     139                 : 
     140           24909 :     if( psInfo->nBufferOffset >= psInfo->nBufferSize )
     141                 :     {
     142             318 :         psInfo->nBufferOffset = 0;
     143             318 :         psInfo->nBufferSize = 
     144             318 :             VSIFReadL( psInfo->pabyBuffer, 1, psInfo->nBufferAllocation,
     145                 :                        psInfo->fp );
     146             318 :         if( psInfo->nBufferSize <= 0 )
     147                 :         {
     148               2 :             if (pbErrorFlag)
     149               2 :                 *pbErrorFlag = TRUE;
     150               2 :             return 0;
     151                 :         }
     152                 :     }
     153                 : 
     154           24907 :     nByte = psInfo->pabyBuffer[psInfo->nBufferOffset++];
     155                 :     
     156           24907 :     if( bNO1 )
     157                 :     {
     158               0 :         nByte = nByte - 9;
     159               0 :         if( nByte < 0 )
     160               0 :             nByte = nByte + 256;
     161                 :     }
     162                 : 
     163           24907 :     return nByte;
     164                 : }
     165                 : 
     166                 : 
     167                 : /************************************************************************/
     168                 : /*                              BSBOpen()                               */
     169                 : /*                                                                      */
     170                 : /*      Read BSB header, and return information.                        */
     171                 : /************************************************************************/
     172                 : 
     173               5 : BSBInfo *BSBOpen( const char *pszFilename )
     174                 : 
     175                 : {
     176                 :     FILE  *fp;
     177                 :     char  achTestBlock[1000];
     178                 :     char        szLine[1000];
     179               5 :     int         i, bNO1 = FALSE;
     180                 :     BSBInfo     *psInfo;
     181               5 :     int    nSkipped = 0;
     182                 :     const char *pszPalette;
     183                 :     int         nOffsetFirstLine;
     184               5 :     int         bErrorFlag = FALSE;
     185                 : 
     186                 : /* -------------------------------------------------------------------- */
     187                 : /*      Which palette do we want to use?                                */
     188                 : /* -------------------------------------------------------------------- */
     189               5 :     pszPalette = CPLGetConfigOption( "BSB_PALETTE", "RGB" );
     190                 : 
     191                 : /* -------------------------------------------------------------------- */
     192                 : /*      Open the file.                                                  */
     193                 : /* -------------------------------------------------------------------- */
     194               5 :     fp = VSIFOpenL( pszFilename, "rb" );
     195               5 :     if( fp == NULL )
     196                 :     {
     197               0 :         CPLError( CE_Failure, CPLE_OpenFailed, 
     198                 :                   "File %s not found.", pszFilename );
     199               0 :         return NULL;
     200                 :     }
     201                 : 
     202                 : /* -------------------------------------------------------------------- */
     203                 : /*  Read the first 1000 bytes, and verify that it contains the  */
     204                 : /*  "BSB/" keyword"              */
     205                 : /* -------------------------------------------------------------------- */
     206               5 :     if( VSIFReadL( achTestBlock, 1, sizeof(achTestBlock), fp ) 
     207                 :         != sizeof(achTestBlock) )
     208                 :     {
     209               0 :         VSIFCloseL( fp );
     210               0 :         CPLError( CE_Failure, CPLE_FileIO,
     211                 :                   "Could not read first %d bytes for header!", 
     212                 :                   (int) sizeof(achTestBlock) );
     213               0 :         return NULL;
     214                 :     }
     215                 : 
     216             140 :     for( i = 0; i < sizeof(achTestBlock) - 4; i++ )
     217                 :     {
     218                 :         /* Test for "BSB/" */
     219             150 :         if( achTestBlock[i+0] == 'B' && achTestBlock[i+1] == 'S' 
     220              10 :             && achTestBlock[i+2] == 'B' && achTestBlock[i+3] == '/' )
     221               5 :             break;
     222                 : 
     223                 :         /* Test for "NOS/" */
     224             135 :         if( achTestBlock[i+0] == 'N' && achTestBlock[i+1] == 'O'
     225               0 :             && achTestBlock[i+2] == 'S' && achTestBlock[i+3] == '/' )
     226               0 :             break;
     227                 : 
     228                 :         /* Test for "NOS/" offset by 9 in ASCII for NO1 files */
     229             135 :         if( achTestBlock[i+0] == 'W' && achTestBlock[i+1] == 'X'
     230               0 :             && achTestBlock[i+2] == '\\' && achTestBlock[i+3] == '8' )
     231                 :         {
     232               0 :             bNO1 = TRUE;
     233               0 :             break;
     234                 :         }
     235                 :     }
     236                 : 
     237               5 :     if( i == sizeof(achTestBlock) - 4 )
     238                 :     {
     239               0 :         VSIFCloseL( fp );
     240               0 :         CPLError( CE_Failure, CPLE_AppDefined, 
     241                 :                   "This does not appear to be a BSB file, no BSB/ header." );
     242               0 :         return NULL;
     243                 :     }
     244                 : 
     245                 : /* -------------------------------------------------------------------- */
     246                 : /*      Create info structure.                                          */
     247                 : /* -------------------------------------------------------------------- */
     248               5 :     psInfo = (BSBInfo *) CPLCalloc(1,sizeof(BSBInfo));
     249               5 :     psInfo->fp = fp;
     250               5 :     psInfo->bNO1 = bNO1;
     251                 : 
     252               5 :     psInfo->nBufferAllocation = 1024;
     253               5 :     psInfo->pabyBuffer = (GByte *) CPLMalloc(psInfo->nBufferAllocation);
     254               5 :     psInfo->nBufferSize = 0; 
     255               5 :     psInfo->nBufferOffset = 0;
     256               5 :     psInfo->nSavedCharacter = -1000;
     257                 : 
     258                 : /* -------------------------------------------------------------------- */
     259                 : /*      Rewind, and read line by line.                                  */
     260                 : /* -------------------------------------------------------------------- */
     261               5 :     VSIFSeekL( fp, 0, SEEK_SET );
     262                 : 
     263             665 :     while( BSBReadHeaderLine(psInfo, szLine, sizeof(szLine), bNO1) )
     264                 :     {
     265             655 :         char  **papszTokens = NULL;
     266             655 :         int      nCount = 0;
     267                 : 
     268             655 :         if( szLine[0] != '\0' && szLine[1] != '\0' && szLine[2] != '\0' && szLine[3] == '/' )
     269                 :         {
     270             650 :             psInfo->papszHeader = CSLAddString( psInfo->papszHeader, szLine );
     271             650 :             papszTokens = CSLTokenizeStringComplex( szLine+4, ",=", 
     272                 :                                                     FALSE,FALSE);
     273             650 :             nCount = CSLCount(papszTokens);
     274                 :         }
     275                 : 
     276             655 :         if( EQUALN(szLine,"BSB/",4) )
     277                 :         {
     278                 :             int   nRAIndex;
     279                 : 
     280               5 :             nRAIndex = CSLFindString(papszTokens, "RA" );
     281               5 :             if( nRAIndex < 0 || nRAIndex+2 >= nCount )
     282                 :             {
     283               0 :                 CSLDestroy( papszTokens );
     284               0 :                 CPLError( CE_Failure, CPLE_AppDefined, 
     285                 :                           "Failed to extract RA from BSB/ line." );
     286               0 :                 BSBClose( psInfo );
     287               0 :                 return NULL;
     288                 :             }
     289               5 :             psInfo->nXSize = atoi(papszTokens[nRAIndex+1]);
     290               5 :             psInfo->nYSize = atoi(papszTokens[nRAIndex+2]);
     291                 :         }
     292             650 :         else if( EQUALN(szLine,"NOS/",4) )
     293                 :         {
     294                 :             int  nRAIndex;
     295                 :             
     296               0 :             nRAIndex = CSLFindString(papszTokens, "RA" );
     297               0 :             if( nRAIndex < 0 || nRAIndex+4 >= nCount )
     298                 :             {
     299               0 :                 CSLDestroy( papszTokens );
     300               0 :                 CPLError( CE_Failure, CPLE_AppDefined,
     301                 :                           "Failed to extract RA from NOS/ line." );
     302               0 :                 BSBClose( psInfo );
     303               0 :                 return NULL;
     304                 :             }
     305               0 :             psInfo->nXSize = atoi(papszTokens[nRAIndex+3]);
     306               0 :             psInfo->nYSize = atoi(papszTokens[nRAIndex+4]);
     307                 :         }
     308            1285 :         else if( EQUALN(szLine, pszPalette, 3) && szLine[3] == '/'
     309                 :                  && nCount >= 4 )
     310                 :         {
     311             635 :             int iPCT = atoi(papszTokens[0]);
     312             635 :             if (iPCT < 0 || iPCT > 128)
     313                 :             {
     314               0 :                 CSLDestroy( papszTokens );
     315               0 :                 CPLError( CE_Failure, CPLE_OutOfMemory, 
     316                 :                             "BSBOpen : Invalid color table index. Probably due to corrupted BSB file (iPCT = %d).",
     317                 :                             iPCT);
     318               0 :                 BSBClose( psInfo );
     319               0 :                 return NULL;
     320                 :             }
     321             635 :             if( iPCT > psInfo->nPCTSize-1 )
     322                 :             {
     323            1270 :                 unsigned char* pabyNewPCT = (unsigned char *) 
     324            1905 :                     VSIRealloc(psInfo->pabyPCT,(iPCT+1) * 3);
     325             635 :                 if (pabyNewPCT == NULL)
     326                 :                 {
     327               0 :                     CSLDestroy( papszTokens );
     328               0 :                     CPLError( CE_Failure, CPLE_OutOfMemory, 
     329                 :                               "BSBOpen : Out of memory. Probably due to corrupted BSB file (iPCT = %d).",
     330                 :                               iPCT);
     331               0 :                     BSBClose( psInfo );
     332               0 :                     return NULL;
     333                 :                 }
     334             635 :                 psInfo->pabyPCT = pabyNewPCT;
     335             635 :                 memset( psInfo->pabyPCT + psInfo->nPCTSize*3, 0, 
     336             635 :                         (iPCT+1-psInfo->nPCTSize) * 3);
     337             635 :                 psInfo->nPCTSize = iPCT+1;
     338                 :             }
     339                 : 
     340             635 :             psInfo->pabyPCT[iPCT*3+0] = (unsigned char)atoi(papszTokens[1]);
     341             635 :             psInfo->pabyPCT[iPCT*3+1] = (unsigned char)atoi(papszTokens[2]);
     342             635 :             psInfo->pabyPCT[iPCT*3+2] = (unsigned char)atoi(papszTokens[3]);
     343                 :         }
     344              15 :         else if( EQUALN(szLine,"VER/",4) && nCount >= 1 )
     345                 :         {
     346               5 :             psInfo->nVersion = (int) (100 * atof(papszTokens[0]) + 0.5);
     347                 :         }
     348                 : 
     349             655 :         CSLDestroy( papszTokens );
     350                 :     }
     351                 :     
     352                 : /* -------------------------------------------------------------------- */
     353                 : /*      Verify we found required keywords.                              */
     354                 : /* -------------------------------------------------------------------- */
     355               5 :     if( psInfo->nXSize == 0 || psInfo->nPCTSize == 0 )
     356                 :     {
     357               0 :         BSBClose( psInfo );
     358               0 :         CPLError( CE_Failure, CPLE_AppDefined, 
     359                 :                   "Failed to find required RGB/ or BSB/ keyword in header." );
     360                 :         
     361               0 :         return NULL;
     362                 :     }
     363                 : 
     364               5 :     if( psInfo->nXSize <= 0 || psInfo->nYSize <= 0 )
     365                 :     {
     366               0 :         BSBClose( psInfo );
     367               0 :         CPLError( CE_Failure, CPLE_AppDefined, 
     368                 :                   "Wrong dimensions found in header : %d x %d.",
     369                 :                   psInfo->nXSize, psInfo->nYSize );
     370               0 :         return NULL;
     371                 :     }
     372                 : 
     373               5 :     if( psInfo->nVersion == 0 )
     374                 :     {
     375               0 :         CPLError( CE_Warning, CPLE_AppDefined,
     376                 :                   "VER (version) keyword not found, assuming 2.0." );
     377               0 :         psInfo->nVersion = 200;
     378                 :     }
     379                 : 
     380                 : /* -------------------------------------------------------------------- */
     381                 : /*      If all has gone well this far, we should be pointing at the     */
     382                 : /*      sequence "0x1A 0x00".  Read past to get to start of data.       */
     383                 : /*                                                                      */
     384                 : /*      We actually do some funny stuff here to be able to read past    */
     385                 : /*      some garbage to try and find the 0x1a 0x00 sequence since in    */
     386                 : /*      at least some files (ie. optech/World.kap) we find a few        */
     387                 : /*      bytes of extra junk in the way.                                 */
     388                 : /* -------------------------------------------------------------------- */
     389                 : /* from optech/World.kap 
     390                 : 
     391                 :    11624: 30333237 34353938 2C302E30 35373836 03274598,0.05786
     392                 :    11640: 39303232 38332C31 332E3135 39363435 902283,13.159645
     393                 :    11656: 35390D0A 1A0D0A1A 00040190 C0510002 59~~~~~~~~~~~Q~~
     394                 :    11672: 90C05100 0390C051 000490C0 51000590 ~~Q~~~~Q~~~~Q~~~
     395                 :  */
     396                 : 
     397                 :     {
     398               5 :         int    nChar = -1;
     399                 : 
     400              20 :         while( nSkipped < 100 
     401                 :               && (BSBGetc( psInfo, bNO1, &bErrorFlag ) != 0x1A 
     402              10 :                   || (nChar = BSBGetc( psInfo, bNO1, &bErrorFlag )) != 0x00) 
     403               0 :               && !bErrorFlag)
     404                 :         {
     405               0 :             if( nChar == 0x1A )
     406                 :             {
     407               0 :                 BSBUngetc( psInfo, nChar );
     408               0 :                 nChar = -1;
     409                 :             }
     410               0 :             nSkipped++;
     411                 :         }
     412                 : 
     413               5 :         if( bErrorFlag )
     414                 :         {
     415               0 :             BSBClose( psInfo );
     416               0 :             CPLError( CE_Failure, CPLE_FileIO, 
     417                 :                         "Truncated BSB file or I/O error." );
     418               0 :             return NULL;
     419                 :         }
     420                 : 
     421               5 :         if( nSkipped == 100 )
     422                 :         {
     423               0 :             BSBClose( psInfo );
     424               0 :             CPLError( CE_Failure, CPLE_AppDefined, 
     425                 :                       "Failed to find compressed data segment of BSB file." );
     426               0 :             return NULL;
     427                 :         }
     428                 :     }
     429                 : 
     430                 : /* -------------------------------------------------------------------- */
     431                 : /*      Read the number of bit size of color numbers.                   */
     432                 : /* -------------------------------------------------------------------- */
     433               5 :     psInfo->nColorSize = BSBGetc( psInfo, bNO1, NULL );
     434                 : 
     435                 :     /* The USGS files like 83116_1.KAP seem to use the ASCII number instead
     436                 :        of the binary number for the colorsize value. */
     437                 :     
     438               5 :     if( nSkipped > 0 
     439               0 :         && psInfo->nColorSize >= 0x31 && psInfo->nColorSize <= 0x38 )
     440               0 :         psInfo->nColorSize -= 0x30;
     441                 : 
     442               5 :     if( ! (psInfo->nColorSize > 0 && psInfo->nColorSize < 9) )
     443                 :     {
     444               0 :         CPLError( CE_Failure, CPLE_AppDefined, 
     445                 :                   "BSBOpen : Bad value for nColorSize (%d). Probably due to corrupted BSB file",
     446                 :                   psInfo->nColorSize );
     447               0 :         BSBClose( psInfo );
     448               0 :         return NULL;
     449                 :     }
     450                 : 
     451                 : /* -------------------------------------------------------------------- */
     452                 : /*      Initialize memory for line offset list.                         */
     453                 : /* -------------------------------------------------------------------- */
     454               5 :     psInfo->panLineOffset = (int *) 
     455               5 :         VSIMalloc2(sizeof(int), psInfo->nYSize);
     456               5 :     if (psInfo->panLineOffset == NULL)
     457                 :     {
     458               0 :         CPLError( CE_Failure, CPLE_OutOfMemory, 
     459                 :                   "BSBOpen : Out of memory. Probably due to corrupted BSB file (nYSize = %d).",
     460                 :                   psInfo->nYSize );
     461               0 :         BSBClose( psInfo );
     462               0 :         return NULL;
     463                 :     }
     464                 : 
     465                 :     /* This is the offset to the data of first line, if there is no index table */
     466               5 :     nOffsetFirstLine = (int)(VSIFTellL( fp ) - psInfo->nBufferSize) + psInfo->nBufferOffset;
     467                 : 
     468                 : /* -------------------------------------------------------------------- */
     469                 : /*       Read the line offset list                                      */
     470                 : /* -------------------------------------------------------------------- */
     471               5 :     if ( ! CSLTestBoolean(CPLGetConfigOption("BSB_DISABLE_INDEX", "NO")) )
     472                 :     {
     473                 :         /* build the list from file's index table */
     474                 :         /* To overcome endian compatibility issues individual
     475                 :          * bytes are being read instead of the whole integers. */
     476                 :         int nVal;
     477               5 :         int listIsOK = 1;
     478                 :         int nOffsetIndexTable;
     479                 :         int nFileLen;
     480                 : 
     481                 :         /* Seek fp to point the last 4 byte integer which points
     482                 :         * the offset of the first line */
     483               5 :         VSIFSeekL( fp, 0, SEEK_END );
     484               5 :         nFileLen = (int)VSIFTellL( fp );
     485               5 :         VSIFSeekL( fp, nFileLen - 4, SEEK_SET );
     486                 : 
     487               5 :         VSIFReadL(&nVal, 1, 4, fp);//last 4 bytes
     488               5 :         CPL_MSBPTR32(&nVal);
     489               5 :         nOffsetIndexTable = nVal;
     490                 : 
     491                 :         /* For some charts, like 1115A_1.KAP, coming from */
     492                 :         /* http://www.nauticalcharts.noaa.gov/mcd/Raster/index.htm, */
     493                 :         /* the index table can have one row less than nYSize */
     494                 :         /* If we look into the file closely, there is no data for */
     495                 :         /* that last row (the end of line psInfo->nYSize - 1 is the start */
     496                 :         /* of the index table), so we can decrement psInfo->nYSize */
     497               5 :         if (nOffsetIndexTable + 4 * (psInfo->nYSize - 1) == nFileLen - 4)
     498                 :         {
     499               0 :             CPLDebug("BSB", "Index size is one row shorter than declared image height. Correct this");
     500               0 :             psInfo->nYSize --;
     501                 :         }
     502                 : 
     503              10 :         if( nOffsetIndexTable <= nOffsetFirstLine ||
     504               5 :             nOffsetIndexTable + 4 * psInfo->nYSize > nFileLen - 4)
     505                 :         {
     506                 :             /* The last 4 bytes are not the value of the offset to the index table */
     507                 :         }
     508               1 :         else if (VSIFSeekL( fp, nOffsetIndexTable, SEEK_SET ) != 0 )
     509                 :         {
     510               0 :             CPLError( CE_Failure, CPLE_FileIO, 
     511                 :                 "Seek to offset 0x%08x for first line offset failed.", 
     512                 :                 nOffsetIndexTable);
     513                 :         }
     514                 :         else
     515                 :         {
     516               1 :             int nIndexSize = (nFileLen - 4 - nOffsetIndexTable) / 4;
     517               1 :             if (nIndexSize != psInfo->nYSize)
     518                 :             {
     519               0 :                 CPLDebug("BSB", "Index size is %d. Expected %d",
     520                 :                         nIndexSize, psInfo->nYSize);
     521                 :             }
     522                 : 
     523              51 :             for(i=0; i < psInfo->nYSize; i++)
     524                 :             {
     525              50 :                 VSIFReadL(&nVal, 1, 4, fp);
     526              50 :                 CPL_MSBPTR32(&nVal);
     527              50 :                 psInfo->panLineOffset[i] = nVal;
     528                 :             }
     529                 :             /* Simple checks for the integrity of the list */
     530              51 :             for(i=0; i < psInfo->nYSize; i++)
     531                 :             {
     532             249 :                 if( psInfo->panLineOffset[i] < nOffsetFirstLine ||
     533              50 :                     psInfo->panLineOffset[i] >= nOffsetIndexTable ||
     534              99 :                     (i < psInfo->nYSize - 1 && psInfo->panLineOffset[i] > psInfo->panLineOffset[i+1]) ||
     535              50 :                     !BSBSeekAndCheckScanlineNumber(psInfo, i, FALSE) )
     536                 :                 {
     537               0 :                     CPLDebug("BSB", "Index table is invalid at index %d", i);
     538               0 :                     listIsOK = 0;
     539               0 :                     break;
     540                 :                 }
     541                 :             }
     542               1 :             if ( listIsOK )
     543                 :             {
     544               1 :                 CPLDebug("BSB", "Index table is valid");
     545               1 :                 return psInfo;
     546                 :             }
     547                 :         }
     548                 :     }
     549                 : 
     550                 :     /* If we can't build the offset list for some reason we just
     551                 :      * initialize the offset list to indicate "no value" (except for the first). */
     552               4 :     psInfo->panLineOffset[0] = nOffsetFirstLine;
     553             200 :     for( i = 1; i < psInfo->nYSize; i++ )
     554             196 :         psInfo->panLineOffset[i] = -1;
     555                 : 
     556               4 :     return psInfo;
     557                 : }
     558                 : 
     559                 : /************************************************************************/
     560                 : /*                         BSBReadHeaderLine()                          */
     561                 : /*                                                                      */
     562                 : /*      Read one virtual line of text from the BSB header.  This        */
     563                 : /*      will end if a 0x1A (EOF) is encountered, indicating the data    */
     564                 : /*      is about to start.  It will also merge multiple physical        */
     565                 : /*      lines where appropriate.                                        */
     566                 : /************************************************************************/
     567                 : 
     568             660 : static int BSBReadHeaderLine( BSBInfo *psInfo, char* pszLine, int nLineMaxLen, int bNO1 )
     569                 : 
     570                 : {
     571                 :     char        chNext;
     572             660 :     int         nLineLen = 0;
     573                 : 
     574           12515 :     while( !VSIFEofL(psInfo->fp) && nLineLen < nLineMaxLen-1 )
     575                 :     {
     576           11855 :         chNext = (char) BSBGetc( psInfo, bNO1, NULL );
     577           11855 :         if( chNext == 0x1A )
     578                 :         {
     579               5 :             BSBUngetc( psInfo, chNext );
     580               5 :             return FALSE;
     581                 :         }
     582                 : 
     583                 :         /* each CR/LF (or LF/CR) as if just "CR" */
     584           11850 :         if( chNext == 10 || chNext == 13 )
     585                 :         {
     586                 :             char  chLF;
     587                 : 
     588             665 :             chLF = (char) BSBGetc( psInfo, bNO1, NULL );
     589             665 :             if( chLF != 10 && chLF != 13 )
     590             665 :                 BSBUngetc( psInfo, chLF );
     591             665 :             chNext = '\n';
     592                 :         }
     593                 : 
     594                 :         /* If we are at the end-of-line, check for blank at start
     595                 :         ** of next line, to indicate need of continuation.
     596                 :         */
     597           11850 :         if( chNext == '\n' )
     598                 :         {
     599                 :             char chTest;
     600                 : 
     601             665 :             chTest = (char) BSBGetc(psInfo, bNO1, NULL);
     602                 :             /* Are we done? */
     603             665 :             if( chTest != ' ' )
     604                 :             {
     605             655 :                 BSBUngetc( psInfo, chTest );
     606             655 :                 pszLine[nLineLen] = '\0';
     607             655 :                 return TRUE;
     608                 :             }
     609                 : 
     610                 :             /* eat pending spaces */
     611              65 :             while( chTest == ' ' )
     612              45 :                 chTest = (char) BSBGetc(psInfo,bNO1, NULL);
     613              10 :             BSBUngetc( psInfo,chTest );
     614                 : 
     615                 :             /* insert comma in data stream */
     616              10 :             pszLine[nLineLen++] = ',';
     617                 :         }
     618                 :         else
     619                 :         {
     620           11185 :             pszLine[nLineLen++] = chNext;
     621                 :         }
     622                 :     }
     623                 : 
     624               0 :     return FALSE;
     625                 : }
     626                 : 
     627                 : /************************************************************************/
     628                 : /*                  BSBSeekAndCheckScanlineNumber()                     */
     629                 : /*                                                                      */
     630                 : /*       Seek to the beginning of the scanline and check that the       */
     631                 : /*       scanline number in file is consistant with what we expect      */
     632                 : /*                                                                      */
     633                 : /* @param nScanline zero based line number                              */
     634                 : /************************************************************************/
     635                 : 
     636             301 : static int BSBSeekAndCheckScanlineNumber ( BSBInfo *psInfo, int nScanline,
     637                 :                                            int bVerboseIfError )
     638                 : {
     639             301 :     int   nLineMarker = 0;
     640                 :     int         byNext;
     641             301 :     FILE  *fp = psInfo->fp;
     642             301 :     int         bErrorFlag = FALSE;
     643                 : 
     644                 : /* -------------------------------------------------------------------- */
     645                 : /*      Seek to requested scanline.                                     */
     646                 : /* -------------------------------------------------------------------- */
     647             301 :     psInfo->nBufferSize = 0;
     648             301 :     if( VSIFSeekL( fp, psInfo->panLineOffset[nScanline], SEEK_SET ) != 0 )
     649                 :     {
     650               0 :         if (bVerboseIfError)
     651                 :         {
     652               0 :             CPLError( CE_Failure, CPLE_FileIO, 
     653                 :                     "Seek to offset %d for scanline %d failed.", 
     654               0 :                     psInfo->panLineOffset[nScanline], nScanline );
     655                 :         }
     656                 :         else
     657                 :         {
     658               0 :             CPLDebug("BSB", "Seek to offset %d for scanline %d failed.", 
     659               0 :                      psInfo->panLineOffset[nScanline], nScanline );
     660                 :         }
     661               0 :         return FALSE;
     662                 :     }
     663                 : 
     664                 : /* -------------------------------------------------------------------- */
     665                 : /*      Read the line number.  Pre 2.0 BSB seemed to expect the line    */
     666                 : /*      numbers to be zero based, while 2.0 and later seemed to         */
     667                 : /*      expect it to be one based, and for a 0 to be some sort of       */
     668                 : /*      missing line marker.                                            */
     669                 : /* -------------------------------------------------------------------- */
     670                 :     do {
     671             301 :         byNext = BSBGetc( psInfo, psInfo->bNO1, &bErrorFlag );
     672                 : 
     673                 :         /* Special hack to skip over extra zeros in some files, such
     674                 :         ** as optech/sample1.kap.
     675                 :         */
     676             602 :         while( nScanline != 0 && nLineMarker == 0 && byNext == 0 && !bErrorFlag )
     677               0 :             byNext = BSBGetc( psInfo, psInfo->bNO1, &bErrorFlag );
     678                 : 
     679             301 :         nLineMarker = nLineMarker * 128 + (byNext & 0x7f);
     680             301 :     } while( (byNext & 0x80) != 0 );
     681                 : 
     682             301 :     if ( bErrorFlag )
     683                 :     {
     684               1 :         if (bVerboseIfError)
     685                 :         {
     686               1 :             CPLError( CE_Failure, CPLE_FileIO, 
     687                 :                     "Truncated BSB file or I/O error." );
     688                 :         }
     689               1 :         return FALSE;
     690                 :     }
     691                 : 
     692             600 :     if( nLineMarker != nScanline 
     693             300 :         && nLineMarker != nScanline + 1 )
     694                 :     {
     695               1 :         if (bVerboseIfError)
     696                 :         {
     697               0 :             CPLError( CE_Failure, CPLE_AppDefined,
     698                 :                      "Got scanline id %d when looking for %d @ offset %d.", 
     699               0 :                      nLineMarker, nScanline+1, psInfo->panLineOffset[nScanline]);
     700                 :         }
     701                 :         else
     702                 :         {
     703               1 :             CPLDebug("BSB", "Got scanline id %d when looking for %d @ offset %d.", 
     704               1 :                      nLineMarker, nScanline+1, psInfo->panLineOffset[nScanline]);
     705                 :         }
     706               1 :         return FALSE;
     707                 :     }
     708                 : 
     709             299 :     return TRUE;
     710                 : }
     711                 : 
     712                 : /************************************************************************/
     713                 : /*                          BSBReadScanline()                           */
     714                 : /* @param nScanline zero based line number                              */
     715                 : /************************************************************************/
     716                 : 
     717             250 : int BSBReadScanline( BSBInfo *psInfo, int nScanline, 
     718                 :                      unsigned char *pabyScanlineBuf )
     719                 : 
     720                 : {
     721             250 :     int   nValueShift, iPixel = 0;
     722                 :     unsigned char byValueMask, byCountMask;
     723             250 :     FILE  *fp = psInfo->fp;
     724                 :     int         byNext, i;
     725                 : 
     726                 : /* -------------------------------------------------------------------- */
     727                 : /*      Do we know where the requested line is?  If not, read all       */
     728                 : /*      the preceeding ones to "find" our line.                         */
     729                 : /* -------------------------------------------------------------------- */
     730             250 :     if( nScanline < 0 || nScanline >= psInfo->nYSize )
     731                 :     {
     732               0 :         CPLError( CE_Failure, CPLE_FileIO, 
     733                 :                   "Scanline %d out of range.", 
     734                 :                    nScanline );
     735               0 :         return FALSE;
     736                 :     }
     737                 : 
     738             250 :     if( psInfo->panLineOffset[nScanline] == -1 )
     739                 :     {
     740               0 :         for( i = 0; i < nScanline; i++ )
     741                 :         {
     742               0 :             if( psInfo->panLineOffset[i+1] == -1 )
     743                 :             {
     744               0 :                 if( !BSBReadScanline( psInfo, i, pabyScanlineBuf ) )
     745               0 :                     return FALSE;
     746                 :             }
     747                 :         }
     748                 :     }
     749                 : 
     750                 : /* -------------------------------------------------------------------- */
     751                 : /*       Seek to the beginning of the scanline and check that the       */
     752                 : /*       scanline number in file is consistant with what we expect      */
     753                 : /* -------------------------------------------------------------------- */
     754             250 :     if ( !BSBSeekAndCheckScanlineNumber(psInfo, nScanline, TRUE) )
     755                 :     {
     756               1 :         return FALSE;
     757                 :     }
     758                 : 
     759                 : /* -------------------------------------------------------------------- */
     760                 : /*      Setup masking values.                                           */
     761                 : /* -------------------------------------------------------------------- */
     762             249 :     nValueShift = 7 - psInfo->nColorSize;
     763             249 :     byValueMask = (unsigned char)
     764             249 :         ((((1 << psInfo->nColorSize)) - 1) << nValueShift);
     765             249 :     byCountMask = (unsigned char)
     766             249 :         (1 << (7 - psInfo->nColorSize)) - 1;
     767                 :     
     768                 : /* -------------------------------------------------------------------- */
     769                 : /*      Read and expand runs.                                           */
     770                 : /*      If for some reason the buffer is not filled,                    */
     771                 : /*      just repeat the process until the buffer is filled.             */
     772                 : /*      This is the case for IS1612_4.NOS (#2782)                       */
     773                 : /* -------------------------------------------------------------------- */
     774                 :     do
     775                 :     {
     776             250 :         int bErrorFlag = FALSE;
     777           25396 :         while( (byNext = BSBGetc(psInfo,psInfo->bNO1, &bErrorFlag)) != 0 &&
     778           12448 :                 !bErrorFlag)
     779                 :         {
     780                 :             int     nPixValue;
     781                 :             int     nRunCount, i;
     782                 : 
     783           12448 :             nPixValue = (byNext & byValueMask) >> nValueShift;
     784                 : 
     785           12448 :             nRunCount = byNext & byCountMask;
     786                 : 
     787           24896 :             while( (byNext & 0x80) != 0 && !bErrorFlag)
     788                 :             {
     789               0 :                 byNext = BSBGetc( psInfo, psInfo->bNO1, &bErrorFlag );
     790               0 :                 nRunCount = nRunCount * 128 + (byNext & 0x7f);
     791                 :             }
     792                 : 
     793                 :             /* Prevent over-run of line data */
     794           12448 :             if (nRunCount < 0 || nRunCount > psInfo->nXSize)
     795                 :             {
     796               0 :                 CPLError( CE_Failure, CPLE_FileIO, 
     797                 :                           "Corrupted run count : %d", nRunCount );
     798               0 :                 return FALSE;
     799                 :             }
     800                 : 
     801           12448 :             if( iPixel + nRunCount + 1 > psInfo->nXSize )
     802               0 :                 nRunCount = psInfo->nXSize - iPixel - 1;
     803                 : 
     804           24896 :             for( i = 0; i < nRunCount+1; i++ )
     805           12448 :                 pabyScanlineBuf[iPixel++] = (unsigned char) nPixValue;
     806                 :         }
     807             250 :         if ( bErrorFlag )
     808                 :         {
     809               1 :             CPLError( CE_Failure, CPLE_FileIO, 
     810                 :                     "Truncated BSB file or I/O error." );
     811               1 :             return FALSE;
     812                 :         }
     813                 : 
     814                 : /* -------------------------------------------------------------------- */
     815                 : /*      For reasons that are unclear, some scanlines are exactly one    */
     816                 : /*      pixel short (such as in the BSB 3.0 354704.KAP product from     */
     817                 : /*      NDI/CHS) but are otherwise OK.  Just add a zero if this         */
     818                 : /*      appear to have occured.                                         */
     819                 : /* -------------------------------------------------------------------- */
     820             249 :         if( iPixel == psInfo->nXSize - 1 )
     821               0 :             pabyScanlineBuf[iPixel++] = 0;
     822                 : 
     823                 : /* -------------------------------------------------------------------- */
     824                 : /*   If we have not enough data and no offset table, check that the     */
     825                 : /*   next bytes are not the expected next scanline number. If they are  */
     826                 : /*   not, then we can use them to fill the row again                    */
     827                 : /* -------------------------------------------------------------------- */
     828             251 :         else if (iPixel < psInfo->nXSize &&
     829               1 :                  nScanline != psInfo->nYSize-1 &&
     830               1 :                  psInfo->panLineOffset[nScanline+1] == -1)
     831                 :         {
     832               1 :             int nCurOffset = (int)(VSIFTellL( fp ) - psInfo->nBufferSize) + 
     833               1 :                                 psInfo->nBufferOffset;
     834               1 :             psInfo->panLineOffset[nScanline+1] = nCurOffset;
     835               1 :             if (BSBSeekAndCheckScanlineNumber(psInfo, nScanline + 1, FALSE))
     836                 :             {
     837               0 :                 CPLDebug("BSB", "iPixel=%d, nScanline=%d, nCurOffset=%d --> found new row marker", iPixel, nScanline, nCurOffset);
     838               0 :                 break;
     839                 :             }
     840                 :             else
     841                 :             {
     842               1 :                 CPLDebug("BSB", "iPixel=%d, nScanline=%d, nCurOffset=%d --> did NOT find new row marker", iPixel, nScanline, nCurOffset);
     843                 : 
     844                 :                 /* The next bytes are not the expected next scanline number, so */
     845                 :                 /* use them to fill the row */
     846               1 :                 VSIFSeekL( fp, nCurOffset, SEEK_SET );
     847               1 :                 psInfo->panLineOffset[nScanline+1] = -1;
     848               1 :                 psInfo->nBufferOffset = 0;
     849               1 :                 psInfo->nBufferSize = 0;
     850                 :             }
     851                 :         }
     852                 :     }
     853             249 :     while ( iPixel < psInfo->nXSize &&
     854               1 :             (nScanline == psInfo->nYSize-1 ||
     855               1 :              psInfo->panLineOffset[nScanline+1] == -1 ||
     856             251 :              VSIFTellL( fp ) - psInfo->nBufferSize + psInfo->nBufferOffset < psInfo->panLineOffset[nScanline+1]) );
     857                 : 
     858                 : /* -------------------------------------------------------------------- */
     859                 : /*      If the line buffer is not filled after reading the line in the  */
     860                 : /*      file upto the next line offset, just fill it with zeros.        */
     861                 : /*      (The last pixel value from nPixValue could be a better value?)  */
     862                 : /* -------------------------------------------------------------------- */
     863             496 :     while( iPixel < psInfo->nXSize )
     864               0 :         pabyScanlineBuf[iPixel++] = 0;
     865                 : 
     866                 : /* -------------------------------------------------------------------- */
     867                 : /*      Remember the start of the next line.                            */
     868                 : /*      But only if it is not already known.                            */
     869                 : /* -------------------------------------------------------------------- */
     870             493 :     if( nScanline < psInfo->nYSize-1 &&
     871             245 :         psInfo->panLineOffset[nScanline+1] == -1 )
     872                 :     {
     873             196 :         psInfo->panLineOffset[nScanline+1] = (int)
     874                 :             (VSIFTellL( fp ) - psInfo->nBufferSize) + psInfo->nBufferOffset;
     875                 :     }
     876                 : 
     877             248 :     return TRUE;
     878                 : }
     879                 : 
     880                 : /************************************************************************/
     881                 : /*                              BSBClose()                              */
     882                 : /************************************************************************/
     883                 : 
     884               5 : void BSBClose( BSBInfo *psInfo )
     885                 : 
     886                 : {
     887               5 :     if( psInfo->fp != NULL )
     888               5 :         VSIFCloseL( psInfo->fp );
     889                 : 
     890               5 :     CPLFree( psInfo->pabyBuffer );
     891                 : 
     892               5 :     CSLDestroy( psInfo->papszHeader );
     893               5 :     CPLFree( psInfo->panLineOffset );
     894               5 :     CPLFree( psInfo->pabyPCT );
     895               5 :     CPLFree( psInfo );
     896               5 : }
     897                 : 
     898                 : /************************************************************************/
     899                 : /*                             BSBCreate()                              */
     900                 : /************************************************************************/
     901                 : 
     902               0 : BSBInfo *BSBCreate( const char *pszFilename, int nCreationFlags, int nVersion, 
     903                 :                     int nXSize, int nYSize )
     904                 : 
     905                 : {
     906                 :     FILE  *fp;
     907                 :     BSBInfo     *psInfo;
     908                 : 
     909                 : /* -------------------------------------------------------------------- */
     910                 : /*      Open new KAP file.                                              */
     911                 : /* -------------------------------------------------------------------- */
     912               0 :     fp = VSIFOpenL( pszFilename, "wb" );
     913               0 :     if( fp == NULL )
     914                 :     {
     915               0 :         CPLError( CE_Failure, CPLE_OpenFailed, 
     916                 :                   "Failed to open output file %s.", 
     917                 :                   pszFilename );
     918               0 :         return NULL;
     919                 :     }
     920                 : 
     921                 : /* -------------------------------------------------------------------- */
     922                 : /*      Write out BSB line.                                             */
     923                 : /* -------------------------------------------------------------------- */
     924               0 :     VSIFPrintfL( fp, 
     925                 :                 "!Copyright unknown\n" );
     926               0 :     VSIFPrintfL( fp, 
     927                 :                 "VER/%.1f\n", nVersion / 100.0 );
     928               0 :     VSIFPrintfL( fp, 
     929                 :                 "BSB/NA=UNKNOWN,NU=999502,RA=%d,%d,DU=254\n",
     930                 :                 nXSize, nYSize );
     931               0 :     VSIFPrintfL( fp, 
     932                 :                 "KNP/SC=25000,GD=WGS84,PR=Mercator\n" );
     933               0 :     VSIFPrintfL( fp, 
     934                 :                 "    PP=31.500000,PI=0.033333,SP=,SK=0.000000,TA=90.000000\n");
     935               0 :     VSIFPrintfL( fp, 
     936                 :                 "     UN=Metres,SD=HHWLT,DX=2.500000,DY=2.500000\n");
     937                 : 
     938                 : 
     939                 : /* -------------------------------------------------------------------- */
     940                 : /*      Create info structure.                                          */
     941                 : /* -------------------------------------------------------------------- */
     942               0 :     psInfo = (BSBInfo *) CPLCalloc(1,sizeof(BSBInfo));
     943               0 :     psInfo->fp = fp;
     944               0 :     psInfo->bNO1 = FALSE;
     945               0 :     psInfo->nVersion = nVersion;
     946               0 :     psInfo->nXSize = nXSize;
     947               0 :     psInfo->nYSize = nYSize;
     948               0 :     psInfo->bNewFile = TRUE;
     949               0 :     psInfo->nLastLineWritten = -1;
     950                 : 
     951               0 :     return psInfo;
     952                 : }
     953                 : 
     954                 : /************************************************************************/
     955                 : /*                            BSBWritePCT()                             */
     956                 : /************************************************************************/
     957                 : 
     958               0 : int BSBWritePCT( BSBInfo *psInfo, int nPCTSize, unsigned char *pabyPCT )
     959                 : 
     960                 : {
     961                 :     int        i;
     962                 :     
     963                 : /* -------------------------------------------------------------------- */
     964                 : /*      Verify the PCT not too large.                                   */
     965                 : /* -------------------------------------------------------------------- */
     966               0 :     if( nPCTSize > 128 )
     967                 :     {
     968               0 :         CPLError( CE_Failure, CPLE_AppDefined, 
     969                 :                   "Pseudo-color table too large (%d entries), at most 128\n"
     970                 :                   " entries allowed in BSB format.", nPCTSize );
     971               0 :         return FALSE;
     972                 :     }
     973                 : 
     974                 : /* -------------------------------------------------------------------- */
     975                 : /*      Compute the number of bits required for the colors.             */
     976                 : /* -------------------------------------------------------------------- */
     977               0 :     for( psInfo->nColorSize = 1; 
     978               0 :          (1 << psInfo->nColorSize) < nPCTSize; 
     979               0 :          psInfo->nColorSize++ ) {}
     980                 : 
     981                 : /* -------------------------------------------------------------------- */
     982                 : /*      Write out the color table.  Note that color table entry zero    */
     983                 : /*      is ignored.  Zero is not a legal value.                         */
     984                 : /* -------------------------------------------------------------------- */
     985               0 :     for( i = 1; i < nPCTSize; i++ )
     986                 :     {
     987               0 :         VSIFPrintfL( psInfo->fp, 
     988                 :                     "RGB/%d,%d,%d,%d\n", 
     989               0 :                     i, pabyPCT[i*3+0], pabyPCT[i*3+1], pabyPCT[i*3+2] );
     990                 :     }
     991                 : 
     992               0 :     return TRUE;
     993                 : }
     994                 : 
     995                 : /************************************************************************/
     996                 : /*                          BSBWriteScanline()                          */
     997                 : /************************************************************************/
     998                 : 
     999               0 : int BSBWriteScanline( BSBInfo *psInfo, unsigned char *pabyScanlineBuf )
    1000                 : 
    1001                 : {
    1002                 :     int   nValue, iX;
    1003                 : 
    1004               0 :     if( psInfo->nLastLineWritten == psInfo->nYSize - 1 )
    1005                 :     {
    1006               0 :         CPLError( CE_Failure, CPLE_AppDefined, 
    1007                 :                   "Attempt to write too many scanlines." );
    1008               0 :         return FALSE;
    1009                 :     }
    1010                 : 
    1011                 : /* -------------------------------------------------------------------- */
    1012                 : /*      If this is the first scanline writen out the EOF marker, and    */
    1013                 : /*      the introductory info in the image segment.                     */
    1014                 : /* -------------------------------------------------------------------- */
    1015               0 :     if( psInfo->nLastLineWritten == -1 )
    1016                 :     {
    1017               0 :         VSIFPutcL( 0x1A, psInfo->fp );
    1018               0 :         VSIFPutcL( 0x00, psInfo->fp );
    1019               0 :         VSIFPutcL( psInfo->nColorSize, psInfo->fp );
    1020                 :     }
    1021                 : 
    1022                 : /* -------------------------------------------------------------------- */
    1023                 : /*      Write the line number.                                          */
    1024                 : /* -------------------------------------------------------------------- */
    1025               0 :     nValue = ++psInfo->nLastLineWritten;
    1026                 : 
    1027               0 :     if( psInfo->nVersion >= 200 )
    1028               0 :         nValue++;
    1029                 : 
    1030               0 :     if( nValue >= 128*128 )
    1031               0 :         VSIFPutcL( 0x80 | ((nValue & (0x7f<<14)) >> 14), psInfo->fp );
    1032               0 :     if( nValue >= 128 )
    1033               0 :         VSIFPutcL( 0x80 | ((nValue & (0x7f<<7)) >> 7), psInfo->fp );
    1034               0 :     VSIFPutcL( nValue & 0x7f, psInfo->fp );
    1035                 : 
    1036                 : /* -------------------------------------------------------------------- */
    1037                 : /*      Write out each pixel as a separate byte.  We don't try to       */
    1038                 : /*      actually capture the runs since that radical and futuristic     */
    1039                 : /*      concept is patented!                                            */
    1040                 : /* -------------------------------------------------------------------- */
    1041               0 :     for( iX = 0; iX < psInfo->nXSize; iX++ )
    1042                 :     {
    1043               0 :         VSIFPutcL( pabyScanlineBuf[iX] << (7-psInfo->nColorSize), 
    1044                 :                     psInfo->fp );
    1045                 :     }
    1046                 : 
    1047               0 :     VSIFPutcL( 0x00, psInfo->fp );
    1048                 : 
    1049               0 :     return TRUE;
    1050                 : }

Generated by: LCOV version 1.7