LCOV - code coverage report
Current view: top level - frmts/bsb - bsb_read.c (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 246 404 60.9 %
Date: 2025-01-18 12:42:00 Functions: 8 11 72.7 %

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

Generated by: LCOV version 1.14