LCOV - code coverage report
Current view: directory - frmts/bsb - bsb_read.c (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 380 223 58.7 %
Date: 2012-12-26 Functions: 10 7 70.0 %

       1                 : /******************************************************************************
       2                 :  * $Id: bsb_read.c 20996 2010-10-28 18:38:15Z rouault $
       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 20996 2010-10-28 18:38:15Z rouault $");
      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            1335 :     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                 :     VSILFILE  *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            1305 :         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               5 :         else if( EQUALN(szLine,"    ",4) && szLine[4] != ' ' )
     276                 :         {
     277                 :             /* add extension lines to the last header line. */
     278               0 :             int iTargetHeader = CSLCount(psInfo->papszHeader);
     279                 : 
     280               0 :             if( iTargetHeader != -1 )
     281                 :             {
     282               0 :                 psInfo->papszHeader[iTargetHeader] = (char *) 
     283               0 :                     CPLRealloc(psInfo->papszHeader[iTargetHeader],
     284               0 :                                strlen(psInfo->papszHeader[iTargetHeader])
     285               0 :                                + strlen(szLine) + 5 );
     286               0 :                 strcat( psInfo->papszHeader[iTargetHeader], "," );
     287               0 :                 strcat( psInfo->papszHeader[iTargetHeader], szLine+4 );
     288                 :             }
     289                 :         }
     290                 : 
     291             655 :         if( EQUALN(szLine,"BSB/",4) )
     292                 :         {
     293                 :             int   nRAIndex;
     294                 : 
     295               5 :             nRAIndex = CSLFindString(papszTokens, "RA" );
     296               5 :             if( nRAIndex < 0 || nRAIndex+2 >= nCount )
     297                 :             {
     298               0 :                 CSLDestroy( papszTokens );
     299               0 :                 CPLError( CE_Failure, CPLE_AppDefined, 
     300                 :                           "Failed to extract RA from BSB/ line." );
     301               0 :                 BSBClose( psInfo );
     302               0 :                 return NULL;
     303                 :             }
     304               5 :             psInfo->nXSize = atoi(papszTokens[nRAIndex+1]);
     305               5 :             psInfo->nYSize = atoi(papszTokens[nRAIndex+2]);
     306                 :         }
     307             650 :         else if( EQUALN(szLine,"NOS/",4) )
     308                 :         {
     309                 :             int  nRAIndex;
     310                 :             
     311               0 :             nRAIndex = CSLFindString(papszTokens, "RA" );
     312               0 :             if( nRAIndex < 0 || nRAIndex+4 >= nCount )
     313                 :             {
     314               0 :                 CSLDestroy( papszTokens );
     315               0 :                 CPLError( CE_Failure, CPLE_AppDefined,
     316                 :                           "Failed to extract RA from NOS/ line." );
     317               0 :                 BSBClose( psInfo );
     318               0 :                 return NULL;
     319                 :             }
     320               0 :             psInfo->nXSize = atoi(papszTokens[nRAIndex+3]);
     321               0 :             psInfo->nYSize = atoi(papszTokens[nRAIndex+4]);
     322                 :         }
     323            1285 :         else if( EQUALN(szLine, pszPalette, 3) && szLine[3] == '/'
     324                 :                  && nCount >= 4 )
     325                 :         {
     326             635 :             int iPCT = atoi(papszTokens[0]);
     327             635 :             if (iPCT < 0 || iPCT > 128)
     328                 :             {
     329               0 :                 CSLDestroy( papszTokens );
     330               0 :                 CPLError( CE_Failure, CPLE_OutOfMemory, 
     331                 :                             "BSBOpen : Invalid color table index. Probably due to corrupted BSB file (iPCT = %d).",
     332                 :                             iPCT);
     333               0 :                 BSBClose( psInfo );
     334               0 :                 return NULL;
     335                 :             }
     336             635 :             if( iPCT > psInfo->nPCTSize-1 )
     337                 :             {
     338            1270 :                 unsigned char* pabyNewPCT = (unsigned char *) 
     339            1905 :                     VSIRealloc(psInfo->pabyPCT,(iPCT+1) * 3);
     340             635 :                 if (pabyNewPCT == NULL)
     341                 :                 {
     342               0 :                     CSLDestroy( papszTokens );
     343               0 :                     CPLError( CE_Failure, CPLE_OutOfMemory, 
     344                 :                               "BSBOpen : Out of memory. Probably due to corrupted BSB file (iPCT = %d).",
     345                 :                               iPCT);
     346               0 :                     BSBClose( psInfo );
     347               0 :                     return NULL;
     348                 :                 }
     349             635 :                 psInfo->pabyPCT = pabyNewPCT;
     350             635 :                 memset( psInfo->pabyPCT + psInfo->nPCTSize*3, 0, 
     351             635 :                         (iPCT+1-psInfo->nPCTSize) * 3);
     352             635 :                 psInfo->nPCTSize = iPCT+1;
     353                 :             }
     354                 : 
     355             635 :             psInfo->pabyPCT[iPCT*3+0] = (unsigned char)atoi(papszTokens[1]);
     356             635 :             psInfo->pabyPCT[iPCT*3+1] = (unsigned char)atoi(papszTokens[2]);
     357             635 :             psInfo->pabyPCT[iPCT*3+2] = (unsigned char)atoi(papszTokens[3]);
     358                 :         }
     359              15 :         else if( EQUALN(szLine,"VER/",4) && nCount >= 1 )
     360                 :         {
     361               5 :             psInfo->nVersion = (int) (100 * atof(papszTokens[0]) + 0.5);
     362                 :         }
     363                 : 
     364             655 :         CSLDestroy( papszTokens );
     365                 :     }
     366                 :     
     367                 : /* -------------------------------------------------------------------- */
     368                 : /*      Verify we found required keywords.                              */
     369                 : /* -------------------------------------------------------------------- */
     370               5 :     if( psInfo->nXSize == 0 || psInfo->nPCTSize == 0 )
     371                 :     {
     372               0 :         BSBClose( psInfo );
     373               0 :         CPLError( CE_Failure, CPLE_AppDefined, 
     374                 :                   "Failed to find required RGB/ or BSB/ keyword in header." );
     375                 :         
     376               0 :         return NULL;
     377                 :     }
     378                 : 
     379               5 :     if( psInfo->nXSize <= 0 || psInfo->nYSize <= 0 )
     380                 :     {
     381               0 :         BSBClose( psInfo );
     382               0 :         CPLError( CE_Failure, CPLE_AppDefined, 
     383                 :                   "Wrong dimensions found in header : %d x %d.",
     384                 :                   psInfo->nXSize, psInfo->nYSize );
     385               0 :         return NULL;
     386                 :     }
     387                 : 
     388               5 :     if( psInfo->nVersion == 0 )
     389                 :     {
     390               0 :         CPLError( CE_Warning, CPLE_AppDefined,
     391                 :                   "VER (version) keyword not found, assuming 2.0." );
     392               0 :         psInfo->nVersion = 200;
     393                 :     }
     394                 : 
     395                 : /* -------------------------------------------------------------------- */
     396                 : /*      If all has gone well this far, we should be pointing at the     */
     397                 : /*      sequence "0x1A 0x00".  Read past to get to start of data.       */
     398                 : /*                                                                      */
     399                 : /*      We actually do some funny stuff here to be able to read past    */
     400                 : /*      some garbage to try and find the 0x1a 0x00 sequence since in    */
     401                 : /*      at least some files (ie. optech/World.kap) we find a few        */
     402                 : /*      bytes of extra junk in the way.                                 */
     403                 : /* -------------------------------------------------------------------- */
     404                 : /* from optech/World.kap 
     405                 : 
     406                 :    11624: 30333237 34353938 2C302E30 35373836 03274598,0.05786
     407                 :    11640: 39303232 38332C31 332E3135 39363435 902283,13.159645
     408                 :    11656: 35390D0A 1A0D0A1A 00040190 C0510002 59~~~~~~~~~~~Q~~
     409                 :    11672: 90C05100 0390C051 000490C0 51000590 ~~Q~~~~Q~~~~Q~~~
     410                 :  */
     411                 : 
     412                 :     {
     413               5 :         int    nChar = -1;
     414                 : 
     415              20 :         while( nSkipped < 100 
     416                 :               && (BSBGetc( psInfo, bNO1, &bErrorFlag ) != 0x1A 
     417              10 :                   || (nChar = BSBGetc( psInfo, bNO1, &bErrorFlag )) != 0x00) 
     418               0 :               && !bErrorFlag)
     419                 :         {
     420               0 :             if( nChar == 0x1A )
     421                 :             {
     422               0 :                 BSBUngetc( psInfo, nChar );
     423               0 :                 nChar = -1;
     424                 :             }
     425               0 :             nSkipped++;
     426                 :         }
     427                 : 
     428               5 :         if( bErrorFlag )
     429                 :         {
     430               0 :             BSBClose( psInfo );
     431               0 :             CPLError( CE_Failure, CPLE_FileIO, 
     432                 :                         "Truncated BSB file or I/O error." );
     433               0 :             return NULL;
     434                 :         }
     435                 : 
     436               5 :         if( nSkipped == 100 )
     437                 :         {
     438               0 :             BSBClose( psInfo );
     439               0 :             CPLError( CE_Failure, CPLE_AppDefined, 
     440                 :                       "Failed to find compressed data segment of BSB file." );
     441               0 :             return NULL;
     442                 :         }
     443                 :     }
     444                 : 
     445                 : /* -------------------------------------------------------------------- */
     446                 : /*      Read the number of bit size of color numbers.                   */
     447                 : /* -------------------------------------------------------------------- */
     448               5 :     psInfo->nColorSize = BSBGetc( psInfo, bNO1, NULL );
     449                 : 
     450                 :     /* The USGS files like 83116_1.KAP seem to use the ASCII number instead
     451                 :        of the binary number for the colorsize value. */
     452                 :     
     453               5 :     if( nSkipped > 0 
     454               0 :         && psInfo->nColorSize >= 0x31 && psInfo->nColorSize <= 0x38 )
     455               0 :         psInfo->nColorSize -= 0x30;
     456                 : 
     457               5 :     if( ! (psInfo->nColorSize > 0 && psInfo->nColorSize < 9) )
     458                 :     {
     459               0 :         CPLError( CE_Failure, CPLE_AppDefined, 
     460                 :                   "BSBOpen : Bad value for nColorSize (%d). Probably due to corrupted BSB file",
     461                 :                   psInfo->nColorSize );
     462               0 :         BSBClose( psInfo );
     463               0 :         return NULL;
     464                 :     }
     465                 : 
     466                 : /* -------------------------------------------------------------------- */
     467                 : /*      Initialize memory for line offset list.                         */
     468                 : /* -------------------------------------------------------------------- */
     469               5 :     psInfo->panLineOffset = (int *) 
     470               5 :         VSIMalloc2(sizeof(int), psInfo->nYSize);
     471               5 :     if (psInfo->panLineOffset == NULL)
     472                 :     {
     473               0 :         CPLError( CE_Failure, CPLE_OutOfMemory, 
     474                 :                   "BSBOpen : Out of memory. Probably due to corrupted BSB file (nYSize = %d).",
     475                 :                   psInfo->nYSize );
     476               0 :         BSBClose( psInfo );
     477               0 :         return NULL;
     478                 :     }
     479                 : 
     480                 :     /* This is the offset to the data of first line, if there is no index table */
     481               5 :     nOffsetFirstLine = (int)(VSIFTellL( fp ) - psInfo->nBufferSize) + psInfo->nBufferOffset;
     482                 : 
     483                 : /* -------------------------------------------------------------------- */
     484                 : /*       Read the line offset list                                      */
     485                 : /* -------------------------------------------------------------------- */
     486               5 :     if ( ! CSLTestBoolean(CPLGetConfigOption("BSB_DISABLE_INDEX", "NO")) )
     487                 :     {
     488                 :         /* build the list from file's index table */
     489                 :         /* To overcome endian compatibility issues individual
     490                 :          * bytes are being read instead of the whole integers. */
     491                 :         int nVal;
     492               5 :         int listIsOK = 1;
     493                 :         int nOffsetIndexTable;
     494                 :         int nFileLen;
     495                 : 
     496                 :         /* Seek fp to point the last 4 byte integer which points
     497                 :         * the offset of the first line */
     498               5 :         VSIFSeekL( fp, 0, SEEK_END );
     499               5 :         nFileLen = (int)VSIFTellL( fp );
     500               5 :         VSIFSeekL( fp, nFileLen - 4, SEEK_SET );
     501                 : 
     502               5 :         VSIFReadL(&nVal, 1, 4, fp);//last 4 bytes
     503               5 :         CPL_MSBPTR32(&nVal);
     504               5 :         nOffsetIndexTable = nVal;
     505                 : 
     506                 :         /* For some charts, like 1115A_1.KAP, coming from */
     507                 :         /* http://www.nauticalcharts.noaa.gov/mcd/Raster/index.htm, */
     508                 :         /* the index table can have one row less than nYSize */
     509                 :         /* If we look into the file closely, there is no data for */
     510                 :         /* that last row (the end of line psInfo->nYSize - 1 is the start */
     511                 :         /* of the index table), so we can decrement psInfo->nYSize */
     512               5 :         if (nOffsetIndexTable + 4 * (psInfo->nYSize - 1) == nFileLen - 4)
     513                 :         {
     514               0 :             CPLDebug("BSB", "Index size is one row shorter than declared image height. Correct this");
     515               0 :             psInfo->nYSize --;
     516                 :         }
     517                 : 
     518              10 :         if( nOffsetIndexTable <= nOffsetFirstLine ||
     519               5 :             nOffsetIndexTable + 4 * psInfo->nYSize > nFileLen - 4)
     520                 :         {
     521                 :             /* The last 4 bytes are not the value of the offset to the index table */
     522                 :         }
     523               1 :         else if (VSIFSeekL( fp, nOffsetIndexTable, SEEK_SET ) != 0 )
     524                 :         {
     525               0 :             CPLError( CE_Failure, CPLE_FileIO, 
     526                 :                 "Seek to offset 0x%08x for first line offset failed.", 
     527                 :                 nOffsetIndexTable);
     528                 :         }
     529                 :         else
     530                 :         {
     531               1 :             int nIndexSize = (nFileLen - 4 - nOffsetIndexTable) / 4;
     532               1 :             if (nIndexSize != psInfo->nYSize)
     533                 :             {
     534               0 :                 CPLDebug("BSB", "Index size is %d. Expected %d",
     535                 :                         nIndexSize, psInfo->nYSize);
     536                 :             }
     537                 : 
     538              51 :             for(i=0; i < psInfo->nYSize; i++)
     539                 :             {
     540              50 :                 VSIFReadL(&nVal, 1, 4, fp);
     541              50 :                 CPL_MSBPTR32(&nVal);
     542              50 :                 psInfo->panLineOffset[i] = nVal;
     543                 :             }
     544                 :             /* Simple checks for the integrity of the list */
     545              51 :             for(i=0; i < psInfo->nYSize; i++)
     546                 :             {
     547             249 :                 if( psInfo->panLineOffset[i] < nOffsetFirstLine ||
     548              50 :                     psInfo->panLineOffset[i] >= nOffsetIndexTable ||
     549              99 :                     (i < psInfo->nYSize - 1 && psInfo->panLineOffset[i] > psInfo->panLineOffset[i+1]) ||
     550              50 :                     !BSBSeekAndCheckScanlineNumber(psInfo, i, FALSE) )
     551                 :                 {
     552               0 :                     CPLDebug("BSB", "Index table is invalid at index %d", i);
     553               0 :                     listIsOK = 0;
     554               0 :                     break;
     555                 :                 }
     556                 :             }
     557               1 :             if ( listIsOK )
     558                 :             {
     559               1 :                 CPLDebug("BSB", "Index table is valid");
     560               1 :                 return psInfo;
     561                 :             }
     562                 :         }
     563                 :     }
     564                 : 
     565                 :     /* If we can't build the offset list for some reason we just
     566                 :      * initialize the offset list to indicate "no value" (except for the first). */
     567               4 :     psInfo->panLineOffset[0] = nOffsetFirstLine;
     568             200 :     for( i = 1; i < psInfo->nYSize; i++ )
     569             196 :         psInfo->panLineOffset[i] = -1;
     570                 : 
     571               4 :     return psInfo;
     572                 : }
     573                 : 
     574                 : /************************************************************************/
     575                 : /*                         BSBReadHeaderLine()                          */
     576                 : /*                                                                      */
     577                 : /*      Read one virtual line of text from the BSB header.  This        */
     578                 : /*      will end if a 0x1A (EOF) is encountered, indicating the data    */
     579                 : /*      is about to start.  It will also merge multiple physical        */
     580                 : /*      lines where appropriate.                                        */
     581                 : /************************************************************************/
     582                 : 
     583             660 : static int BSBReadHeaderLine( BSBInfo *psInfo, char* pszLine, int nLineMaxLen, int bNO1 )
     584                 : 
     585                 : {
     586                 :     char        chNext;
     587             660 :     int         nLineLen = 0;
     588                 : 
     589           12515 :     while( !VSIFEofL(psInfo->fp) && nLineLen < nLineMaxLen-1 )
     590                 :     {
     591           11855 :         chNext = (char) BSBGetc( psInfo, bNO1, NULL );
     592           11855 :         if( chNext == 0x1A )
     593                 :         {
     594               5 :             BSBUngetc( psInfo, chNext );
     595               5 :             return FALSE;
     596                 :         }
     597                 : 
     598                 :         /* each CR/LF (or LF/CR) as if just "CR" */
     599           11850 :         if( chNext == 10 || chNext == 13 )
     600                 :         {
     601                 :             char  chLF;
     602                 : 
     603             665 :             chLF = (char) BSBGetc( psInfo, bNO1, NULL );
     604             665 :             if( chLF != 10 && chLF != 13 )
     605             665 :                 BSBUngetc( psInfo, chLF );
     606             665 :             chNext = '\n';
     607                 :         }
     608                 : 
     609                 :         /* If we are at the end-of-line, check for blank at start
     610                 :         ** of next line, to indicate need of continuation.
     611                 :         */
     612           11850 :         if( chNext == '\n' )
     613                 :         {
     614                 :             char chTest;
     615                 : 
     616             665 :             chTest = (char) BSBGetc(psInfo, bNO1, NULL);
     617                 :             /* Are we done? */
     618             665 :             if( chTest != ' ' )
     619                 :             {
     620             655 :                 BSBUngetc( psInfo, chTest );
     621             655 :                 pszLine[nLineLen] = '\0';
     622             655 :                 return TRUE;
     623                 :             }
     624                 : 
     625                 :             /* eat pending spaces */
     626              65 :             while( chTest == ' ' )
     627              45 :                 chTest = (char) BSBGetc(psInfo,bNO1, NULL);
     628              10 :             BSBUngetc( psInfo,chTest );
     629                 : 
     630                 :             /* insert comma in data stream */
     631              10 :             pszLine[nLineLen++] = ',';
     632                 :         }
     633                 :         else
     634                 :         {
     635           11185 :             pszLine[nLineLen++] = chNext;
     636                 :         }
     637                 :     }
     638                 : 
     639               0 :     return FALSE;
     640                 : }
     641                 : 
     642                 : /************************************************************************/
     643                 : /*                  BSBSeekAndCheckScanlineNumber()                     */
     644                 : /*                                                                      */
     645                 : /*       Seek to the beginning of the scanline and check that the       */
     646                 : /*       scanline number in file is consistant with what we expect      */
     647                 : /*                                                                      */
     648                 : /* @param nScanline zero based line number                              */
     649                 : /************************************************************************/
     650                 : 
     651             301 : static int BSBSeekAndCheckScanlineNumber ( BSBInfo *psInfo, int nScanline,
     652                 :                                            int bVerboseIfError )
     653                 : {
     654             301 :     int   nLineMarker = 0;
     655                 :     int         byNext;
     656             301 :     VSILFILE  *fp = psInfo->fp;
     657             301 :     int         bErrorFlag = FALSE;
     658                 : 
     659                 : /* -------------------------------------------------------------------- */
     660                 : /*      Seek to requested scanline.                                     */
     661                 : /* -------------------------------------------------------------------- */
     662             301 :     psInfo->nBufferSize = 0;
     663             301 :     if( VSIFSeekL( fp, psInfo->panLineOffset[nScanline], SEEK_SET ) != 0 )
     664                 :     {
     665               0 :         if (bVerboseIfError)
     666                 :         {
     667               0 :             CPLError( CE_Failure, CPLE_FileIO, 
     668                 :                     "Seek to offset %d for scanline %d failed.", 
     669               0 :                     psInfo->panLineOffset[nScanline], nScanline );
     670                 :         }
     671                 :         else
     672                 :         {
     673               0 :             CPLDebug("BSB", "Seek to offset %d for scanline %d failed.", 
     674               0 :                      psInfo->panLineOffset[nScanline], nScanline );
     675                 :         }
     676               0 :         return FALSE;
     677                 :     }
     678                 : 
     679                 : /* -------------------------------------------------------------------- */
     680                 : /*      Read the line number.  Pre 2.0 BSB seemed to expect the line    */
     681                 : /*      numbers to be zero based, while 2.0 and later seemed to         */
     682                 : /*      expect it to be one based, and for a 0 to be some sort of       */
     683                 : /*      missing line marker.                                            */
     684                 : /* -------------------------------------------------------------------- */
     685                 :     do {
     686             301 :         byNext = BSBGetc( psInfo, psInfo->bNO1, &bErrorFlag );
     687                 : 
     688                 :         /* Special hack to skip over extra zeros in some files, such
     689                 :         ** as optech/sample1.kap.
     690                 :         */
     691             602 :         while( nScanline != 0 && nLineMarker == 0 && byNext == 0 && !bErrorFlag )
     692               0 :             byNext = BSBGetc( psInfo, psInfo->bNO1, &bErrorFlag );
     693                 : 
     694             301 :         nLineMarker = nLineMarker * 128 + (byNext & 0x7f);
     695             301 :     } while( (byNext & 0x80) != 0 );
     696                 : 
     697             301 :     if ( bErrorFlag )
     698                 :     {
     699               1 :         if (bVerboseIfError)
     700                 :         {
     701               1 :             CPLError( CE_Failure, CPLE_FileIO, 
     702                 :                     "Truncated BSB file or I/O error." );
     703                 :         }
     704               1 :         return FALSE;
     705                 :     }
     706             600 :     if( nLineMarker != nScanline 
     707             300 :         && nLineMarker != nScanline + 1 )
     708                 :     {
     709                 :         int bIgnoreLineNumbers = 
     710               1 :             CSLTestBoolean(CPLGetConfigOption("BSB_IGNORE_LINENUMBERS", "NO"));
     711                 : 
     712               1 :         if (bVerboseIfError && !bIgnoreLineNumbers )
     713                 :         {
     714               0 :             CPLError( CE_Failure, CPLE_AppDefined,
     715                 :                      "Got scanline id %d when looking for %d @ offset %d.\nSet BSB_IGNORE_LINENUMBERS=TRUE configuration option to try file anyways.", 
     716               0 :                      nLineMarker, nScanline+1, psInfo->panLineOffset[nScanline]);
     717                 :         }
     718                 :         else
     719                 :         {
     720               1 :             CPLDebug("BSB", "Got scanline id %d when looking for %d @ offset %d.", 
     721               1 :                      nLineMarker, nScanline+1, psInfo->panLineOffset[nScanline]);
     722                 :         }
     723                 : 
     724               1 :         if( !bIgnoreLineNumbers )
     725               1 :             return FALSE;
     726                 :     }
     727                 : 
     728             299 :     return TRUE;
     729                 : }
     730                 : 
     731                 : /************************************************************************/
     732                 : /*                          BSBReadScanline()                           */
     733                 : /* @param nScanline zero based line number                              */
     734                 : /************************************************************************/
     735                 : 
     736             250 : int BSBReadScanline( BSBInfo *psInfo, int nScanline, 
     737                 :                      unsigned char *pabyScanlineBuf )
     738                 : 
     739                 : {
     740             250 :     int   nValueShift, iPixel = 0;
     741                 :     unsigned char byValueMask, byCountMask;
     742             250 :     VSILFILE  *fp = psInfo->fp;
     743                 :     int         byNext, i;
     744                 : 
     745                 : /* -------------------------------------------------------------------- */
     746                 : /*      Do we know where the requested line is?  If not, read all       */
     747                 : /*      the preceeding ones to "find" our line.                         */
     748                 : /* -------------------------------------------------------------------- */
     749             250 :     if( nScanline < 0 || nScanline >= psInfo->nYSize )
     750                 :     {
     751               0 :         CPLError( CE_Failure, CPLE_FileIO, 
     752                 :                   "Scanline %d out of range.", 
     753                 :                    nScanline );
     754               0 :         return FALSE;
     755                 :     }
     756                 : 
     757             250 :     if( psInfo->panLineOffset[nScanline] == -1 )
     758                 :     {
     759               0 :         for( i = 0; i < nScanline; i++ )
     760                 :         {
     761               0 :             if( psInfo->panLineOffset[i+1] == -1 )
     762                 :             {
     763               0 :                 if( !BSBReadScanline( psInfo, i, pabyScanlineBuf ) )
     764               0 :                     return FALSE;
     765                 :             }
     766                 :         }
     767                 :     }
     768                 : 
     769                 : /* -------------------------------------------------------------------- */
     770                 : /*       Seek to the beginning of the scanline and check that the       */
     771                 : /*       scanline number in file is consistant with what we expect      */
     772                 : /* -------------------------------------------------------------------- */
     773             250 :     if ( !BSBSeekAndCheckScanlineNumber(psInfo, nScanline, TRUE) )
     774                 :     {
     775               1 :         return FALSE;
     776                 :     }
     777                 : 
     778                 : /* -------------------------------------------------------------------- */
     779                 : /*      Setup masking values.                                           */
     780                 : /* -------------------------------------------------------------------- */
     781             249 :     nValueShift = 7 - psInfo->nColorSize;
     782             249 :     byValueMask = (unsigned char)
     783             249 :         ((((1 << psInfo->nColorSize)) - 1) << nValueShift);
     784             249 :     byCountMask = (unsigned char)
     785             249 :         (1 << (7 - psInfo->nColorSize)) - 1;
     786                 :     
     787                 : /* -------------------------------------------------------------------- */
     788                 : /*      Read and expand runs.                                           */
     789                 : /*      If for some reason the buffer is not filled,                    */
     790                 : /*      just repeat the process until the buffer is filled.             */
     791                 : /*      This is the case for IS1612_4.NOS (#2782)                       */
     792                 : /* -------------------------------------------------------------------- */
     793                 :     do
     794                 :     {
     795             250 :         int bErrorFlag = FALSE;
     796           25396 :         while( (byNext = BSBGetc(psInfo,psInfo->bNO1, &bErrorFlag)) != 0 &&
     797           12448 :                 !bErrorFlag)
     798                 :         {
     799                 :             int     nPixValue;
     800                 :             int     nRunCount, i;
     801                 : 
     802           12448 :             nPixValue = (byNext & byValueMask) >> nValueShift;
     803                 : 
     804           12448 :             nRunCount = byNext & byCountMask;
     805                 : 
     806           24896 :             while( (byNext & 0x80) != 0 && !bErrorFlag)
     807                 :             {
     808               0 :                 byNext = BSBGetc( psInfo, psInfo->bNO1, &bErrorFlag );
     809               0 :                 nRunCount = nRunCount * 128 + (byNext & 0x7f);
     810                 :             }
     811                 : 
     812                 :             /* Prevent over-run of line data */
     813           12448 :             if (nRunCount < 0 || nRunCount > INT_MAX - (iPixel + 1))
     814                 :             {
     815               0 :                 CPLError( CE_Failure, CPLE_FileIO, 
     816                 :                           "Corrupted run count : %d", nRunCount );
     817               0 :                 return FALSE;
     818                 :             }
     819           12448 :             if (nRunCount > psInfo->nXSize)
     820                 :             {
     821                 :                 static int bHasWarned = FALSE;
     822               0 :                 if (!bHasWarned)
     823                 :                 {
     824               0 :                     CPLDebug("BSB", "Too big run count : %d", nRunCount );
     825               0 :                     bHasWarned = TRUE;
     826                 :                 }
     827                 :             }
     828                 : 
     829           12448 :             if( iPixel + nRunCount + 1 > psInfo->nXSize )
     830               0 :                 nRunCount = psInfo->nXSize - iPixel - 1;
     831                 : 
     832           24896 :             for( i = 0; i < nRunCount+1; i++ )
     833           12448 :                 pabyScanlineBuf[iPixel++] = (unsigned char) nPixValue;
     834                 :         }
     835             250 :         if ( bErrorFlag )
     836                 :         {
     837               1 :             CPLError( CE_Failure, CPLE_FileIO, 
     838                 :                     "Truncated BSB file or I/O error." );
     839               1 :             return FALSE;
     840                 :         }
     841                 : 
     842                 : /* -------------------------------------------------------------------- */
     843                 : /*      For reasons that are unclear, some scanlines are exactly one    */
     844                 : /*      pixel short (such as in the BSB 3.0 354704.KAP product from     */
     845                 : /*      NDI/CHS) but are otherwise OK.  Just add a zero if this         */
     846                 : /*      appear to have occured.                                         */
     847                 : /* -------------------------------------------------------------------- */
     848             249 :         if( iPixel == psInfo->nXSize - 1 )
     849               0 :             pabyScanlineBuf[iPixel++] = 0;
     850                 : 
     851                 : /* -------------------------------------------------------------------- */
     852                 : /*   If we have not enough data and no offset table, check that the     */
     853                 : /*   next bytes are not the expected next scanline number. If they are  */
     854                 : /*   not, then we can use them to fill the row again                    */
     855                 : /* -------------------------------------------------------------------- */
     856             251 :         else if (iPixel < psInfo->nXSize &&
     857               1 :                  nScanline != psInfo->nYSize-1 &&
     858               1 :                  psInfo->panLineOffset[nScanline+1] == -1)
     859                 :         {
     860               1 :             int nCurOffset = (int)(VSIFTellL( fp ) - psInfo->nBufferSize) + 
     861               1 :                                 psInfo->nBufferOffset;
     862               1 :             psInfo->panLineOffset[nScanline+1] = nCurOffset;
     863               1 :             if (BSBSeekAndCheckScanlineNumber(psInfo, nScanline + 1, FALSE))
     864                 :             {
     865               0 :                 CPLDebug("BSB", "iPixel=%d, nScanline=%d, nCurOffset=%d --> found new row marker", iPixel, nScanline, nCurOffset);
     866               0 :                 break;
     867                 :             }
     868                 :             else
     869                 :             {
     870               1 :                 CPLDebug("BSB", "iPixel=%d, nScanline=%d, nCurOffset=%d --> did NOT find new row marker", iPixel, nScanline, nCurOffset);
     871                 : 
     872                 :                 /* The next bytes are not the expected next scanline number, so */
     873                 :                 /* use them to fill the row */
     874               1 :                 VSIFSeekL( fp, nCurOffset, SEEK_SET );
     875               1 :                 psInfo->panLineOffset[nScanline+1] = -1;
     876               1 :                 psInfo->nBufferOffset = 0;
     877               1 :                 psInfo->nBufferSize = 0;
     878                 :             }
     879                 :         }
     880                 :     }
     881             249 :     while ( iPixel < psInfo->nXSize &&
     882               1 :             (nScanline == psInfo->nYSize-1 ||
     883               1 :              psInfo->panLineOffset[nScanline+1] == -1 ||
     884             251 :              VSIFTellL( fp ) - psInfo->nBufferSize + psInfo->nBufferOffset < psInfo->panLineOffset[nScanline+1]) );
     885                 : 
     886                 : /* -------------------------------------------------------------------- */
     887                 : /*      If the line buffer is not filled after reading the line in the  */
     888                 : /*      file upto the next line offset, just fill it with zeros.        */
     889                 : /*      (The last pixel value from nPixValue could be a better value?)  */
     890                 : /* -------------------------------------------------------------------- */
     891             496 :     while( iPixel < psInfo->nXSize )
     892               0 :         pabyScanlineBuf[iPixel++] = 0;
     893                 : 
     894                 : /* -------------------------------------------------------------------- */
     895                 : /*      Remember the start of the next line.                            */
     896                 : /*      But only if it is not already known.                            */
     897                 : /* -------------------------------------------------------------------- */
     898             493 :     if( nScanline < psInfo->nYSize-1 &&
     899             245 :         psInfo->panLineOffset[nScanline+1] == -1 )
     900                 :     {
     901             196 :         psInfo->panLineOffset[nScanline+1] = (int)
     902                 :             (VSIFTellL( fp ) - psInfo->nBufferSize) + psInfo->nBufferOffset;
     903                 :     }
     904                 : 
     905             248 :     return TRUE;
     906                 : }
     907                 : 
     908                 : /************************************************************************/
     909                 : /*                              BSBClose()                              */
     910                 : /************************************************************************/
     911                 : 
     912               5 : void BSBClose( BSBInfo *psInfo )
     913                 : 
     914                 : {
     915               5 :     if( psInfo->fp != NULL )
     916               5 :         VSIFCloseL( psInfo->fp );
     917                 : 
     918               5 :     CPLFree( psInfo->pabyBuffer );
     919                 : 
     920               5 :     CSLDestroy( psInfo->papszHeader );
     921               5 :     CPLFree( psInfo->panLineOffset );
     922               5 :     CPLFree( psInfo->pabyPCT );
     923               5 :     CPLFree( psInfo );
     924               5 : }
     925                 : 
     926                 : /************************************************************************/
     927                 : /*                             BSBCreate()                              */
     928                 : /************************************************************************/
     929                 : 
     930               0 : BSBInfo *BSBCreate( const char *pszFilename, int nCreationFlags, int nVersion, 
     931                 :                     int nXSize, int nYSize )
     932                 : 
     933                 : {
     934                 :     VSILFILE  *fp;
     935                 :     BSBInfo     *psInfo;
     936                 : 
     937                 : /* -------------------------------------------------------------------- */
     938                 : /*      Open new KAP file.                                              */
     939                 : /* -------------------------------------------------------------------- */
     940               0 :     fp = VSIFOpenL( pszFilename, "wb" );
     941               0 :     if( fp == NULL )
     942                 :     {
     943               0 :         CPLError( CE_Failure, CPLE_OpenFailed, 
     944                 :                   "Failed to open output file %s.", 
     945                 :                   pszFilename );
     946               0 :         return NULL;
     947                 :     }
     948                 : 
     949                 : /* -------------------------------------------------------------------- */
     950                 : /*      Write out BSB line.                                             */
     951                 : /* -------------------------------------------------------------------- */
     952               0 :     VSIFPrintfL( fp, 
     953                 :                 "!Copyright unknown\n" );
     954               0 :     VSIFPrintfL( fp, 
     955                 :                 "VER/%.1f\n", nVersion / 100.0 );
     956               0 :     VSIFPrintfL( fp, 
     957                 :                 "BSB/NA=UNKNOWN,NU=999502,RA=%d,%d,DU=254\n",
     958                 :                 nXSize, nYSize );
     959               0 :     VSIFPrintfL( fp, 
     960                 :                 "KNP/SC=25000,GD=WGS84,PR=Mercator\n" );
     961               0 :     VSIFPrintfL( fp, 
     962                 :                 "    PP=31.500000,PI=0.033333,SP=,SK=0.000000,TA=90.000000\n");
     963               0 :     VSIFPrintfL( fp, 
     964                 :                 "     UN=Metres,SD=HHWLT,DX=2.500000,DY=2.500000\n");
     965                 : 
     966                 : 
     967                 : /* -------------------------------------------------------------------- */
     968                 : /*      Create info structure.                                          */
     969                 : /* -------------------------------------------------------------------- */
     970               0 :     psInfo = (BSBInfo *) CPLCalloc(1,sizeof(BSBInfo));
     971               0 :     psInfo->fp = fp;
     972               0 :     psInfo->bNO1 = FALSE;
     973               0 :     psInfo->nVersion = nVersion;
     974               0 :     psInfo->nXSize = nXSize;
     975               0 :     psInfo->nYSize = nYSize;
     976               0 :     psInfo->bNewFile = TRUE;
     977               0 :     psInfo->nLastLineWritten = -1;
     978                 : 
     979               0 :     return psInfo;
     980                 : }
     981                 : 
     982                 : /************************************************************************/
     983                 : /*                            BSBWritePCT()                             */
     984                 : /************************************************************************/
     985                 : 
     986               0 : int BSBWritePCT( BSBInfo *psInfo, int nPCTSize, unsigned char *pabyPCT )
     987                 : 
     988                 : {
     989                 :     int        i;
     990                 :     
     991                 : /* -------------------------------------------------------------------- */
     992                 : /*      Verify the PCT not too large.                                   */
     993                 : /* -------------------------------------------------------------------- */
     994               0 :     if( nPCTSize > 128 )
     995                 :     {
     996               0 :         CPLError( CE_Failure, CPLE_AppDefined, 
     997                 :                   "Pseudo-color table too large (%d entries), at most 128\n"
     998                 :                   " entries allowed in BSB format.", nPCTSize );
     999               0 :         return FALSE;
    1000                 :     }
    1001                 : 
    1002                 : /* -------------------------------------------------------------------- */
    1003                 : /*      Compute the number of bits required for the colors.             */
    1004                 : /* -------------------------------------------------------------------- */
    1005               0 :     for( psInfo->nColorSize = 1; 
    1006               0 :          (1 << psInfo->nColorSize) < nPCTSize; 
    1007               0 :          psInfo->nColorSize++ ) {}
    1008                 : 
    1009                 : /* -------------------------------------------------------------------- */
    1010                 : /*      Write out the color table.  Note that color table entry zero    */
    1011                 : /*      is ignored.  Zero is not a legal value.                         */
    1012                 : /* -------------------------------------------------------------------- */
    1013               0 :     for( i = 1; i < nPCTSize; i++ )
    1014                 :     {
    1015               0 :         VSIFPrintfL( psInfo->fp, 
    1016                 :                     "RGB/%d,%d,%d,%d\n", 
    1017               0 :                     i, pabyPCT[i*3+0], pabyPCT[i*3+1], pabyPCT[i*3+2] );
    1018                 :     }
    1019                 : 
    1020               0 :     return TRUE;
    1021                 : }
    1022                 : 
    1023                 : /************************************************************************/
    1024                 : /*                          BSBWriteScanline()                          */
    1025                 : /************************************************************************/
    1026                 : 
    1027               0 : int BSBWriteScanline( BSBInfo *psInfo, unsigned char *pabyScanlineBuf )
    1028                 : 
    1029                 : {
    1030                 :     int   nValue, iX;
    1031                 : 
    1032               0 :     if( psInfo->nLastLineWritten == psInfo->nYSize - 1 )
    1033                 :     {
    1034               0 :         CPLError( CE_Failure, CPLE_AppDefined, 
    1035                 :                   "Attempt to write too many scanlines." );
    1036               0 :         return FALSE;
    1037                 :     }
    1038                 : 
    1039                 : /* -------------------------------------------------------------------- */
    1040                 : /*      If this is the first scanline writen out the EOF marker, and    */
    1041                 : /*      the introductory info in the image segment.                     */
    1042                 : /* -------------------------------------------------------------------- */
    1043               0 :     if( psInfo->nLastLineWritten == -1 )
    1044                 :     {
    1045               0 :         VSIFPutcL( 0x1A, psInfo->fp );
    1046               0 :         VSIFPutcL( 0x00, psInfo->fp );
    1047               0 :         VSIFPutcL( psInfo->nColorSize, psInfo->fp );
    1048                 :     }
    1049                 : 
    1050                 : /* -------------------------------------------------------------------- */
    1051                 : /*      Write the line number.                                          */
    1052                 : /* -------------------------------------------------------------------- */
    1053               0 :     nValue = ++psInfo->nLastLineWritten;
    1054                 : 
    1055               0 :     if( psInfo->nVersion >= 200 )
    1056               0 :         nValue++;
    1057                 : 
    1058               0 :     if( nValue >= 128*128 )
    1059               0 :         VSIFPutcL( 0x80 | ((nValue & (0x7f<<14)) >> 14), psInfo->fp );
    1060               0 :     if( nValue >= 128 )
    1061               0 :         VSIFPutcL( 0x80 | ((nValue & (0x7f<<7)) >> 7), psInfo->fp );
    1062               0 :     VSIFPutcL( nValue & 0x7f, psInfo->fp );
    1063                 : 
    1064                 : /* -------------------------------------------------------------------- */
    1065                 : /*      Write out each pixel as a separate byte.  We don't try to       */
    1066                 : /*      actually capture the runs since that radical and futuristic     */
    1067                 : /*      concept is patented!                                            */
    1068                 : /* -------------------------------------------------------------------- */
    1069               0 :     for( iX = 0; iX < psInfo->nXSize; iX++ )
    1070                 :     {
    1071               0 :         VSIFPutcL( pabyScanlineBuf[iX] << (7-psInfo->nColorSize), 
    1072                 :                     psInfo->fp );
    1073                 :     }
    1074                 : 
    1075               0 :     VSIFPutcL( 0x00, psInfo->fp );
    1076                 : 
    1077               0 :     return TRUE;
    1078                 : }

Generated by: LCOV version 1.7