LCOV - code coverage report
Current view: top level - frmts/bsb - bsb_read.c (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 246 406 60.6 %
Date: 2024-11-21 22:18:42 Functions: 8 11 72.7 %

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

Generated by: LCOV version 1.14