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

Generated by: LCOV version 1.14