Coverage Report

Created: 2025-08-11 09:23

/src/gdal/ogr/ogrsf_frmts/selafin/io_selafin.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 * Project:  Selafin importer
3
 * Purpose:  Implementation of functions for reading records in Selafin files
4
 * Author:   François Hissel, francois.hissel@gmail.com
5
 *
6
 ******************************************************************************
7
 * Copyright (c) 2014,  François Hissel <francois.hissel@gmail.com>
8
 *
9
 * SPDX-License-Identifier: MIT
10
 ****************************************************************************/
11
12
#include "io_selafin.h"
13
#include "cpl_vsi.h"
14
#include "cpl_conv.h"
15
#include "cpl_port.h"
16
#include "cpl_error.h"
17
#include "cpl_quad_tree.h"
18
19
namespace Selafin
20
{
21
22
const char SELAFIN_ERROR_MESSAGE[] = "Error when reading Selafin file\n";
23
24
struct Point
25
{
26
    int nIndex;
27
    const Header *poHeader;
28
};
29
30
static void GetBoundsFunc(const void *hFeature, CPLRectObj *poBounds)
31
0
{
32
0
    const Point *poPoint = (const Point *)hFeature;
33
0
    poBounds->minx = poPoint->poHeader->paadfCoords[0][poPoint->nIndex];
34
0
    poBounds->maxx = poPoint->poHeader->paadfCoords[0][poPoint->nIndex];
35
0
    poBounds->miny = poPoint->poHeader->paadfCoords[1][poPoint->nIndex];
36
0
    poBounds->maxy = poPoint->poHeader->paadfCoords[1][poPoint->nIndex];
37
0
}
38
39
static int DumpFeatures(void *pElt, void * /* pUserData */)
40
0
{
41
0
    Point *poPoint = (Point *)pElt;
42
0
    delete poPoint;
43
0
    return TRUE;
44
0
}
45
46
/****************************************************************/
47
/*                         Header                               */
48
/****************************************************************/
49
Header::Header()
50
278
    : nHeaderSize(0), nStepSize(0), nMinxIndex(-1), nMaxxIndex(-1),
51
278
      nMinyIndex(-1), nMaxyIndex(-1), bTreeUpdateNeeded(true), nFileSize(0),
52
278
      fp(nullptr), pszFilename(nullptr), pszTitle(nullptr), nVar(0),
53
278
      papszVariables(nullptr), nPoints(0), nElements(0), nPointsPerElement(0),
54
278
      panConnectivity(nullptr), poTree(nullptr), panBorder(nullptr),
55
278
      panStartDate(nullptr), nSteps(0), nEpsg(0)
56
278
{
57
278
    paadfCoords[0] = nullptr;
58
278
    paadfCoords[1] = nullptr;
59
2.22k
    for (size_t i = 0; i < 7; ++i)
60
1.94k
        anUnused[i] = 0;
61
278
    adfOrigin[0] = 0.0;
62
278
    adfOrigin[1] = 0.0;
63
278
}
64
65
Header::~Header()
66
278
{
67
278
    CPLFree(pszFilename);
68
278
    CPLFree(pszTitle);
69
278
    if (papszVariables != nullptr)
70
224
    {
71
536
        for (int i = 0; i < nVar; ++i)
72
312
            CPLFree(papszVariables[i]);
73
224
        CPLFree(papszVariables);
74
224
    }
75
278
    CPLFree(panConnectivity);
76
278
    CPLFree(panBorder);
77
278
    if (poTree != nullptr)
78
0
    {
79
0
        CPLQuadTreeForeach(poTree, DumpFeatures, nullptr);
80
0
        CPLQuadTreeDestroy(poTree);
81
0
    }
82
278
    CPLFree(panStartDate);
83
834
    for (size_t i = 0; i < 2; ++i)
84
556
        CPLFree(paadfCoords[i]);
85
278
    if (fp != nullptr)
86
278
        VSIFCloseL(fp);
87
278
}
88
89
void Header::setUpdated()
90
87
{
91
87
    nHeaderSize = 88 + 16 + nVar * 40 + 12 * 4 +
92
87
                  ((panStartDate == nullptr) ? 0 : 32) + 24 +
93
87
                  (nElements * nPointsPerElement + 2) * 4 + (nPoints + 2) * 12;
94
87
    nStepSize = 12 + nVar * (nPoints + 2) * 4;
95
87
}
96
97
int Header::getPosition(int nStep, int nFeature, int nAttribute) const
98
166k
{
99
166k
    int a = (nFeature != -1 || nAttribute != -1)
100
166k
                ? (12 + nAttribute * (nPoints + 2) * 4 + 4 + nFeature * 4)
101
166k
                : 0;
102
166k
    int b = nStep * nStepSize;
103
166k
    return nHeaderSize + b + a;
104
166k
}
105
106
CPLRectObj *Header::getBoundingBox() const
107
0
{
108
0
    CPLRectObj *poBox = new CPLRectObj;
109
0
    poBox->minx = paadfCoords[0][nMinxIndex];
110
0
    poBox->maxx = paadfCoords[0][nMaxxIndex];
111
0
    poBox->miny = paadfCoords[1][nMinyIndex];
112
0
    poBox->maxy = paadfCoords[1][nMaxyIndex];
113
0
    return poBox;
114
0
}
115
116
void Header::updateBoundingBox()
117
87
{
118
87
    if (nPoints > 0)
119
0
    {
120
0
        nMinxIndex = 0;
121
0
        for (int i = 1; i < nPoints; ++i)
122
0
            if (paadfCoords[0][i] < paadfCoords[0][nMinxIndex])
123
0
                nMinxIndex = i;
124
0
        nMaxxIndex = 0;
125
0
        for (int i = 1; i < nPoints; ++i)
126
0
            if (paadfCoords[0][i] > paadfCoords[0][nMaxxIndex])
127
0
                nMaxxIndex = i;
128
0
        nMinyIndex = 0;
129
0
        for (int i = 1; i < nPoints; ++i)
130
0
            if (paadfCoords[1][i] < paadfCoords[1][nMinyIndex])
131
0
                nMinyIndex = i;
132
0
        nMaxyIndex = 0;
133
0
        for (int i = 1; i < nPoints; ++i)
134
0
            if (paadfCoords[1][i] > paadfCoords[1][nMaxyIndex])
135
0
                nMaxyIndex = i;
136
0
    }
137
87
}
138
139
int Header::getClosestPoint(const double &dfx, const double &dfy,
140
                            const double &dfMax)
141
0
{
142
    // If there is no quad-tree of the points, build it now
143
0
    if (bTreeUpdateNeeded)
144
0
    {
145
0
        if (poTree != nullptr)
146
0
        {
147
0
            CPLQuadTreeForeach(poTree, DumpFeatures, nullptr);
148
0
            CPLQuadTreeDestroy(poTree);
149
0
        }
150
0
    }
151
0
    if (bTreeUpdateNeeded || poTree == nullptr)
152
0
    {
153
0
        bTreeUpdateNeeded = false;
154
0
        CPLRectObj *poBB = getBoundingBox();
155
0
        poTree = CPLQuadTreeCreate(poBB, GetBoundsFunc);
156
0
        delete poBB;
157
0
        CPLQuadTreeSetBucketCapacity(poTree, 2);
158
0
        for (int i = 0; i < nPoints; ++i)
159
0
        {
160
0
            Point *poPoint = new Point;
161
0
            poPoint->poHeader = this;
162
0
            poPoint->nIndex = i;
163
0
            CPLQuadTreeInsert(poTree, (void *)poPoint);
164
0
        }
165
0
    }
166
    // Now we can look for the nearest neighbour using this tree
167
0
    int nIndex = -1;
168
0
    CPLRectObj poObj;
169
0
    poObj.minx = dfx - dfMax;
170
0
    poObj.maxx = dfx + dfMax;
171
0
    poObj.miny = dfy - dfMax;
172
0
    poObj.maxy = dfy + dfMax;
173
0
    int nFeatureCount = 0;
174
0
    void **phResults = CPLQuadTreeSearch(poTree, &poObj, &nFeatureCount);
175
0
    if (nFeatureCount <= 0)
176
0
        return -1;
177
0
    double dfMin = dfMax * dfMax;
178
0
    for (int i = 0; i < nFeatureCount; ++i)
179
0
    {
180
0
        Point *poPoint = (Point *)(phResults[i]);
181
0
        double dfa = dfx - poPoint->poHeader->paadfCoords[0][poPoint->nIndex];
182
0
        dfa *= dfa;
183
0
        if (dfa >= dfMin)
184
0
            continue;
185
0
        const double dfb =
186
0
            dfy - poPoint->poHeader->paadfCoords[1][poPoint->nIndex];
187
0
        const double dfc = dfa + dfb * dfb;
188
0
        if (dfc < dfMin)
189
0
        {
190
0
            dfMin = dfc;
191
0
            nIndex = poPoint->nIndex;
192
0
        }
193
0
    }
194
0
    CPLFree(phResults);
195
0
    return nIndex;
196
0
}
197
198
void Header::addPoint(const double &dfx, const double &dfy)
199
0
{
200
    // We add the point to all the tables
201
0
    nPoints++;
202
0
    for (size_t i = 0; i < 2; ++i)
203
0
        paadfCoords[i] =
204
0
            (double *)CPLRealloc(paadfCoords[i], sizeof(double) * nPoints);
205
0
    paadfCoords[0][nPoints - 1] = dfx;
206
0
    paadfCoords[1][nPoints - 1] = dfy;
207
0
    panBorder = (int *)CPLRealloc(panBorder, sizeof(int) * nPoints);
208
0
    panBorder[nPoints - 1] = 0;
209
    // We update the bounding box
210
0
    if (nMinxIndex == -1 || dfx < paadfCoords[0][nMinxIndex])
211
0
        nMinxIndex = nPoints - 1;
212
0
    if (nMaxxIndex == -1 || dfx > paadfCoords[0][nMaxxIndex])
213
0
        nMaxxIndex = nPoints - 1;
214
0
    if (nMinyIndex == -1 || dfy < paadfCoords[1][nMinyIndex])
215
0
        nMinyIndex = nPoints - 1;
216
0
    if (nMaxyIndex == -1 || dfy > paadfCoords[1][nMaxyIndex])
217
0
        nMaxyIndex = nPoints - 1;
218
    // We update some parameters of the header
219
0
    bTreeUpdateNeeded = true;
220
0
    setUpdated();
221
0
}
222
223
void Header::removePoint(int nIndex)
224
0
{
225
    // We remove the point from all the tables
226
0
    nPoints--;
227
0
    for (size_t i = 0; i < 2; ++i)
228
0
    {
229
0
        for (int j = nIndex; j < nPoints; ++j)
230
0
            paadfCoords[i][j] = paadfCoords[i][j + 1];
231
0
        paadfCoords[i] =
232
0
            (double *)CPLRealloc(paadfCoords[i], sizeof(double) * nPoints);
233
0
    }
234
0
    for (int j = nIndex; j < nPoints; ++j)
235
0
        panBorder[j] = panBorder[j + 1];
236
0
    panBorder = (int *)CPLRealloc(panBorder, sizeof(int) * nPoints);
237
238
    // We must also remove all the elements referencing the deleted feature,
239
    // otherwise the file will not be consistent any inter
240
0
    int nOldElements = nElements;
241
0
    for (int i = 0; i < nElements; ++i)
242
0
    {
243
0
        bool bReferencing = false;
244
0
        int *panTemp = panConnectivity + i * nPointsPerElement;
245
0
        for (int j = 0; j < nPointsPerElement; ++j)
246
0
            bReferencing |= (panTemp[j] == nIndex + 1);
247
0
        if (bReferencing)
248
0
        {
249
0
            nElements--;
250
0
            for (int j = i; j < nElements; ++j)
251
0
                for (int k = 0; k < nPointsPerElement; ++k)
252
0
                    panConnectivity[j * nPointsPerElement + k] =
253
0
                        panConnectivity[(j + 1) * nPointsPerElement + k];
254
0
            --i;
255
0
        }
256
0
    }
257
0
    if (nOldElements != nElements)
258
0
        panConnectivity = (int *)CPLRealloc(
259
0
            panConnectivity, sizeof(int) * nElements * nPointsPerElement);
260
261
    // Now we update the bounding box if needed
262
0
    if (nPoints == 0)
263
0
    {
264
0
        nMinxIndex = -1;
265
0
        nMaxxIndex = -1;
266
0
        nMinyIndex = -1;
267
0
        nMaxyIndex = -1;
268
0
    }
269
0
    else
270
0
    {
271
0
        if (nIndex == nMinxIndex)
272
0
        {
273
0
            nMinxIndex = 0;
274
0
            for (int i = 1; i < nPoints; ++i)
275
0
                if (paadfCoords[0][i] < paadfCoords[0][nMinxIndex])
276
0
                    nMinxIndex = i;
277
0
        }
278
0
        if (nIndex == nMaxxIndex)
279
0
        {
280
0
            nMaxxIndex = 0;
281
0
            for (int i = 1; i < nPoints; ++i)
282
0
                if (paadfCoords[0][i] > paadfCoords[0][nMaxxIndex])
283
0
                    nMaxxIndex = i;
284
0
        }
285
0
        if (nIndex == nMinyIndex)
286
0
        {
287
0
            nMinyIndex = 0;
288
0
            for (int i = 1; i < nPoints; ++i)
289
0
                if (paadfCoords[1][i] < paadfCoords[1][nMinyIndex])
290
0
                    nMinyIndex = i;
291
0
        }
292
0
        if (nIndex == nMaxyIndex)
293
0
        {
294
0
            nMaxyIndex = 0;
295
0
            for (int i = 1; i < nPoints; ++i)
296
0
                if (paadfCoords[1][i] > paadfCoords[1][nMaxyIndex])
297
0
                    nMaxyIndex = i;
298
0
        }
299
0
    }
300
301
    // We update some parameters of the header
302
0
    bTreeUpdateNeeded = true;
303
0
    setUpdated();
304
0
}
305
306
#ifdef notdef
307
/****************************************************************/
308
/*                         TimeStep                             */
309
/****************************************************************/
310
TimeStep::TimeStep(int nRecordsP, int nFieldsP) : nFields(nFieldsP)
311
{
312
    papadfData = (double **)VSI_MALLOC2_VERBOSE(sizeof(double *), nFieldsP);
313
    for (int i = 0; i < nFieldsP; ++i)
314
        papadfData[i] =
315
            (double *)VSI_MALLOC2_VERBOSE(sizeof(double), nRecordsP);
316
}
317
318
TimeStep::~TimeStep()
319
{
320
    for (int i = 0; i < nFields; ++i)
321
        CPLFree(papadfData[i]);
322
    CPLFree(papadfData);
323
}
324
325
/****************************************************************/
326
/*                       TimeStepList                           */
327
/****************************************************************/
328
TimeStepList::~TimeStepList()
329
{
330
    TimeStepList *poFirst = this;
331
    while (poFirst != 0)
332
    {
333
        TimeStepList *poTmp = poFirst->poNext;
334
        delete poFirst->poStep;
335
        delete poFirst;
336
        poFirst = poTmp;
337
    }
338
}
339
#endif
340
341
/****************************************************************/
342
/*                     General functions                        */
343
/****************************************************************/
344
int read_integer(VSILFILE *fp, int &nData, bool bDiscard)
345
163k
{
346
163k
    unsigned char anb[4];
347
163k
    if (VSIFReadL(anb, 1, 4, fp) < 4)
348
81
    {
349
81
        CPLError(CE_Failure, CPLE_FileIO, "%s", SELAFIN_ERROR_MESSAGE);
350
81
        return 0;
351
163k
    };
352
163k
    if (!bDiscard)
353
163k
    {
354
163k
        memcpy(&nData, anb, 4);
355
163k
        CPL_MSBPTR32(&nData);
356
163k
    }
357
163k
    return 1;
358
163k
}
359
360
int write_integer(VSILFILE *fp, int nData)
361
0
{
362
0
    unsigned char anb[4];
363
0
    CPL_MSBPTR32(&nData);
364
0
    memcpy(anb, &nData, 4);
365
0
    if (VSIFWriteL(anb, 1, 4, fp) < 4)
366
0
    {
367
0
        CPLError(CE_Failure, CPLE_FileIO, "%s", SELAFIN_ERROR_MESSAGE);
368
0
        return 0;
369
0
    };
370
0
    return 1;
371
0
}
372
373
int read_string(VSILFILE *fp, char *&pszData, vsi_l_offset nFileSize,
374
                bool bDiscard)
375
674
{
376
674
    int nLength = 0;
377
674
    read_integer(fp, nLength);
378
674
    if (nLength <= 0 || nLength == INT_MAX ||
379
674
        static_cast<unsigned>(nLength) > nFileSize)
380
78
    {
381
78
        CPLError(CE_Failure, CPLE_FileIO, "%s", SELAFIN_ERROR_MESSAGE);
382
78
        return 0;
383
78
    }
384
596
    if (bDiscard)
385
0
    {
386
0
        if (VSIFSeekL(fp, nLength + 4, SEEK_CUR) != 0)
387
0
        {
388
0
            CPLError(CE_Failure, CPLE_FileIO, "%s", SELAFIN_ERROR_MESSAGE);
389
0
            return 0;
390
0
        }
391
0
    }
392
596
    else
393
596
    {
394
596
        pszData = (char *)VSI_MALLOC_VERBOSE(nLength + 1);
395
596
        if (pszData == nullptr)
396
0
        {
397
0
            return 0;
398
0
        }
399
596
        if ((int)VSIFReadL(pszData, 1, nLength, fp) < (int)nLength)
400
6
        {
401
6
            CPLError(CE_Failure, CPLE_FileIO, "%s", SELAFIN_ERROR_MESSAGE);
402
6
            VSIFree(pszData);
403
6
            pszData = nullptr;
404
6
            return 0;
405
6
        }
406
590
        pszData[nLength] = 0;
407
590
        if (VSIFSeekL(fp, 4, SEEK_CUR) != 0)
408
0
        {
409
0
            CPLError(CE_Failure, CPLE_FileIO, "%s", SELAFIN_ERROR_MESSAGE);
410
0
            VSIFree(pszData);
411
0
            pszData = nullptr;
412
0
            return 0;
413
0
        }
414
590
    }
415
590
    return nLength;
416
596
}
417
418
int write_string(VSILFILE *fp, char *pszData, size_t nLength)
419
0
{
420
0
    if (nLength == 0)
421
0
        nLength = strlen(pszData);
422
0
    if (write_integer(fp, static_cast<int>(nLength)) == 0)
423
0
        return 0;
424
0
    if (VSIFWriteL(pszData, 1, nLength, fp) < nLength)
425
0
    {
426
0
        CPLError(CE_Failure, CPLE_FileIO, "%s", SELAFIN_ERROR_MESSAGE);
427
0
        return 0;
428
0
    }
429
0
    if (write_integer(fp, static_cast<int>(nLength)) == 0)
430
0
        return 0;
431
0
    return 1;
432
0
}
433
434
int read_intarray(VSILFILE *fp, int *&panData, vsi_l_offset nFileSize,
435
                  bool bDiscard)
436
813
{
437
813
    int nLength = 0;
438
813
    read_integer(fp, nLength);
439
813
    panData = nullptr;
440
813
    if (nLength < 0 || static_cast<unsigned>(nLength) / 4 > nFileSize)
441
82
    {
442
82
        CPLError(CE_Failure, CPLE_FileIO, "%s", SELAFIN_ERROR_MESSAGE);
443
82
        return -1;
444
82
    }
445
731
    if (bDiscard)
446
0
    {
447
0
        if (VSIFSeekL(fp, nLength + 4, SEEK_CUR) != 0)
448
0
        {
449
0
            CPLError(CE_Failure, CPLE_FileIO, "%s", SELAFIN_ERROR_MESSAGE);
450
0
            return -1;
451
0
        }
452
0
    }
453
731
    else
454
731
    {
455
731
        if (nLength == 0)
456
147
            panData = nullptr;
457
584
        else
458
584
        {
459
584
            panData = (int *)VSI_MALLOC2_VERBOSE(nLength / 4, sizeof(int));
460
584
            if (panData == nullptr)
461
5
                return -1;
462
584
        }
463
163k
        for (int i = 0; i < nLength / 4; ++i)
464
162k
            if (read_integer(fp, panData[i]) == 0)
465
22
            {
466
22
                CPLFree(panData);
467
22
                panData = nullptr;
468
22
                CPLError(CE_Failure, CPLE_FileIO, "%s", SELAFIN_ERROR_MESSAGE);
469
22
                return -1;
470
22
            }
471
704
        if (VSIFSeekL(fp, 4, SEEK_CUR) != 0)
472
0
        {
473
0
            CPLFree(panData);
474
0
            panData = nullptr;
475
0
            CPLError(CE_Failure, CPLE_FileIO, "%s", SELAFIN_ERROR_MESSAGE);
476
0
            return -1;
477
0
        }
478
704
    }
479
704
    return nLength / 4;
480
731
}
481
482
int write_intarray(VSILFILE *fp, int *panData, size_t nLength)
483
0
{
484
0
    if (write_integer(fp, static_cast<int>(nLength * 4)) == 0)
485
0
        return 0;
486
0
    for (size_t i = 0; i < nLength; ++i)
487
0
    {
488
0
        if (write_integer(fp, panData[i]) == 0)
489
0
        {
490
0
            CPLError(CE_Failure, CPLE_FileIO, "%s", SELAFIN_ERROR_MESSAGE);
491
0
            return 0;
492
0
        }
493
0
    }
494
0
    if (write_integer(fp, static_cast<int>(nLength * 4)) == 0)
495
0
        return 0;
496
0
    return 1;
497
0
}
498
499
int read_float(VSILFILE *fp, double &dfData, bool bDiscard)
500
190k
{
501
190k
    float dfVal = 0.0;
502
190k
    if (VSIFReadL(&dfVal, 1, 4, fp) < 4)
503
24
    {
504
24
        CPLError(CE_Failure, CPLE_FileIO, "%s", SELAFIN_ERROR_MESSAGE);
505
24
        return 0;
506
190k
    };
507
190k
    if (!bDiscard)
508
190k
    {
509
190k
        CPL_MSBPTR32(&dfVal);
510
190k
        dfData = dfVal;
511
190k
    }
512
190k
    return 1;
513
190k
}
514
515
int write_float(VSILFILE *fp, double dfData)
516
0
{
517
0
    float dfVal = (float)dfData;
518
0
    CPL_MSBPTR32(&dfVal);
519
0
    if (VSIFWriteL(&dfVal, 1, 4, fp) < 4)
520
0
    {
521
0
        CPLError(CE_Failure, CPLE_FileIO, "%s", SELAFIN_ERROR_MESSAGE);
522
0
        return 0;
523
0
    };
524
0
    return 1;
525
0
}
526
527
int read_floatarray(VSILFILE *fp, double **papadfData, vsi_l_offset nFileSize,
528
                    bool bDiscard)
529
174
{
530
174
    int nLength = 0;
531
174
    read_integer(fp, nLength);
532
174
    if (nLength < 0 || static_cast<unsigned>(nLength) / 4 > nFileSize)
533
74
    {
534
74
        CPLError(CE_Failure, CPLE_FileIO, "%s", SELAFIN_ERROR_MESSAGE);
535
74
        return -1;
536
74
    }
537
100
    if (bDiscard)
538
0
    {
539
0
        if (VSIFSeekL(fp, nLength + 4, SEEK_CUR) != 0)
540
0
        {
541
0
            CPLError(CE_Failure, CPLE_FileIO, "%s", SELAFIN_ERROR_MESSAGE);
542
0
            return -1;
543
0
        }
544
0
    }
545
100
    else
546
100
    {
547
100
        if (nLength == 0)
548
49
            *papadfData = nullptr;
549
51
        else
550
51
        {
551
51
            *papadfData =
552
51
                (double *)VSI_MALLOC2_VERBOSE(sizeof(double), nLength / 4);
553
51
            if (*papadfData == nullptr)
554
2
                return -1;
555
51
        }
556
24.7k
        for (int i = 0; i < nLength / 4; ++i)
557
24.6k
            if (read_float(fp, (*papadfData)[i]) == 0)
558
24
            {
559
24
                CPLFree(*papadfData);
560
24
                *papadfData = nullptr;
561
24
                CPLError(CE_Failure, CPLE_FileIO, "%s", SELAFIN_ERROR_MESSAGE);
562
24
                return -1;
563
24
            }
564
74
        if (VSIFSeekL(fp, 4, SEEK_CUR) != 0)
565
0
        {
566
0
            CPLFree(*papadfData);
567
0
            *papadfData = nullptr;
568
0
            CPLError(CE_Failure, CPLE_FileIO, "%s", SELAFIN_ERROR_MESSAGE);
569
0
            return -1;
570
0
        }
571
74
    }
572
74
    return nLength / 4;
573
100
}
574
575
int write_floatarray(VSILFILE *fp, double *papadfData, size_t nLength)
576
0
{
577
0
    if (write_integer(fp, static_cast<int>(nLength * 4)) == 0)
578
0
        return 0;
579
0
    for (size_t i = 0; i < nLength; ++i)
580
0
    {
581
0
        if (write_float(fp, papadfData[i]) == 0)
582
0
        {
583
0
            CPLError(CE_Failure, CPLE_FileIO, "%s", SELAFIN_ERROR_MESSAGE);
584
0
            return 0;
585
0
        }
586
0
    }
587
0
    if (write_integer(fp, static_cast<int>(nLength * 4)) == 0)
588
0
        return 0;
589
0
    return 1;
590
0
}
591
592
void Header::UpdateFileSize()
593
278
{
594
278
    VSIFSeekL(fp, 0, SEEK_END);
595
278
    nFileSize = VSIFTellL(fp);
596
278
    VSIRewindL(fp);
597
278
}
598
599
Header *read_header(VSILFILE *fp, const char *pszFilename)
600
278
{
601
    // Save the filename
602
278
    Header *poHeader = new Header();
603
278
    poHeader->fp = fp;
604
278
    poHeader->UpdateFileSize();
605
278
    poHeader->pszFilename = CPLStrdup(pszFilename);
606
278
    int *panTemp = nullptr;
607
    // Read the title
608
278
    int nLength = read_string(fp, poHeader->pszTitle, poHeader->nFileSize);
609
278
    if (nLength == 0)
610
0
    {
611
0
        delete poHeader;
612
0
        return nullptr;
613
0
    }
614
    // Read the array of 2 integers, with the number of variables at the first
615
    // position
616
278
    nLength = read_intarray(fp, panTemp, poHeader->nFileSize);
617
278
    if (nLength != 2)
618
4
    {
619
4
        delete poHeader;
620
4
        CPLFree(panTemp);
621
4
        return nullptr;
622
4
    }
623
274
    poHeader->nVar = panTemp[0];
624
274
    poHeader->anUnused[0] = panTemp[1];
625
274
    CPLFree(panTemp);
626
274
    if (poHeader->nVar < 0)
627
3
    {
628
3
        poHeader->nVar = 0;
629
3
        delete poHeader;
630
3
        return nullptr;
631
3
    }
632
271
    if (poHeader->nVar > 1000000 && poHeader->nFileSize / sizeof(int) <
633
6
                                        static_cast<unsigned>(poHeader->nVar))
634
6
    {
635
6
        poHeader->nVar = 0;
636
6
        delete poHeader;
637
6
        return nullptr;
638
6
    }
639
    // For each variable, read its name as a string of 32 characters
640
265
    poHeader->papszVariables =
641
265
        (char **)VSI_MALLOC2_VERBOSE(sizeof(char *), poHeader->nVar);
642
265
    if (poHeader->nVar > 0 && poHeader->papszVariables == nullptr)
643
0
    {
644
0
        poHeader->nVar = 0;
645
0
        delete poHeader;
646
0
        return nullptr;
647
0
    }
648
577
    for (int i = 0; i < poHeader->nVar; ++i)
649
396
    {
650
396
        nLength =
651
396
            read_string(fp, poHeader->papszVariables[i], poHeader->nFileSize);
652
396
        if (nLength == 0)
653
84
        {
654
84
            poHeader->nVar = i;
655
84
            delete poHeader;
656
84
            return nullptr;
657
84
        }
658
        // We eliminate quotes in the names of the variables because SQL
659
        // requests don't seem to appreciate them
660
312
        char *pszc = poHeader->papszVariables[i];
661
64.2k
        while (*pszc != 0)
662
63.9k
        {
663
63.9k
            if (*pszc == '\'')
664
194
                *pszc = ' ';
665
63.9k
            pszc++;
666
63.9k
        }
667
312
    }
668
    // Read an array of 10 integers
669
181
    nLength = read_intarray(fp, panTemp, poHeader->nFileSize);
670
181
    if (nLength < 10)
671
47
    {
672
47
        delete poHeader;
673
47
        CPLFree(panTemp);
674
47
        return nullptr;
675
47
    }
676
134
    poHeader->anUnused[1] = panTemp[0];
677
134
    poHeader->nEpsg = panTemp[1];
678
134
    poHeader->adfOrigin[0] = panTemp[2];
679
134
    poHeader->adfOrigin[1] = panTemp[3];
680
804
    for (size_t i = 4; i < 9; ++i)
681
670
        poHeader->anUnused[i - 2] = panTemp[i];
682
    // If the last integer was 1, read an array of 6 integers with the starting
683
    // date
684
134
    if (panTemp[9] == 1)
685
30
    {
686
30
        nLength =
687
30
            read_intarray(fp, poHeader->panStartDate, poHeader->nFileSize);
688
30
        if (nLength < 6)
689
3
        {
690
3
            delete poHeader;
691
3
            CPLFree(panTemp);
692
3
            return nullptr;
693
3
        }
694
30
    }
695
131
    CPLFree(panTemp);
696
    // Read an array of 4 integers with the number of elements, points and
697
    // points per element
698
131
    nLength = read_intarray(fp, panTemp, poHeader->nFileSize);
699
131
    if (nLength < 4)
700
15
    {
701
15
        delete poHeader;
702
15
        CPLFree(panTemp);
703
15
        return nullptr;
704
15
    }
705
116
    poHeader->nElements = panTemp[0];
706
116
    poHeader->nPoints = panTemp[1];
707
116
    poHeader->nPointsPerElement = panTemp[2];
708
116
    if (poHeader->nElements < 0 || poHeader->nPoints < 0 ||
709
116
        poHeader->nPointsPerElement < 0 || panTemp[3] != 1)
710
16
    {
711
16
        delete poHeader;
712
16
        CPLFree(panTemp);
713
16
        return nullptr;
714
16
    }
715
100
    CPLFree(panTemp);
716
    // Read the connectivity table as an array of nPointsPerElement*nElements
717
    // integers, and check if all point numbers are valid
718
100
    nLength = read_intarray(fp, poHeader->panConnectivity, poHeader->nFileSize);
719
100
    if (poHeader->nElements != 0 &&
720
100
        nLength / poHeader->nElements != poHeader->nPointsPerElement)
721
7
    {
722
7
        delete poHeader;
723
7
        return nullptr;
724
7
    }
725
93
    for (int i = 0; i < poHeader->nElements * poHeader->nPointsPerElement; ++i)
726
0
    {
727
0
        if (poHeader->panConnectivity[i] <= 0 ||
728
0
            poHeader->panConnectivity[i] > poHeader->nPoints)
729
0
        {
730
0
            delete poHeader;
731
0
            return nullptr;
732
0
        }
733
0
    }
734
    // Read the array of nPoints integers with the border points
735
93
    nLength = read_intarray(fp, poHeader->panBorder, poHeader->nFileSize);
736
93
    if (nLength != poHeader->nPoints)
737
6
    {
738
6
        delete poHeader;
739
6
        return nullptr;
740
6
    }
741
    // Read two arrays of nPoints floats with the coordinates of each point
742
261
    for (size_t i = 0; i < 2; ++i)
743
174
    {
744
174
        read_floatarray(fp, poHeader->paadfCoords + i, poHeader->nFileSize);
745
174
        if (nLength < poHeader->nPoints)
746
0
        {
747
0
            delete poHeader;
748
0
            return nullptr;
749
0
        }
750
174
        if (poHeader->nPoints != 0 && poHeader->paadfCoords[i] == nullptr)
751
0
        {
752
0
            delete poHeader;
753
0
            return nullptr;
754
0
        }
755
174
        for (int j = 0; j < poHeader->nPoints; ++j)
756
0
            poHeader->paadfCoords[i][j] += poHeader->adfOrigin[i];
757
174
    }
758
    // Update the boundinx box
759
87
    poHeader->updateBoundingBox();
760
    // Update the size of the header and calculate the number of time steps
761
87
    poHeader->setUpdated();
762
87
    int nPos = poHeader->getPosition(0);
763
87
    if (static_cast<vsi_l_offset>(nPos) > poHeader->nFileSize)
764
0
    {
765
0
        delete poHeader;
766
0
        return nullptr;
767
0
    }
768
87
    vsi_l_offset nStepsBig =
769
87
        poHeader->nVar != 0
770
87
            ? (poHeader->nFileSize - nPos) / (poHeader->getPosition(1) - nPos)
771
87
            : 0;
772
87
    if (nStepsBig > INT_MAX)
773
0
        poHeader->nSteps = INT_MAX;
774
87
    else
775
87
        poHeader->nSteps = static_cast<int>(nStepsBig);
776
87
    return poHeader;
777
87
}
778
779
int write_header(VSILFILE *fp, Header *poHeader)
780
0
{
781
0
    VSIRewindL(fp);
782
0
    if (write_string(fp, poHeader->pszTitle, 80) == 0)
783
0
        return 0;
784
0
    int anTemp[10] = {0};
785
0
    anTemp[0] = poHeader->nVar;
786
0
    anTemp[1] = poHeader->anUnused[0];
787
0
    if (write_intarray(fp, anTemp, 2) == 0)
788
0
        return 0;
789
0
    for (int i = 0; i < poHeader->nVar; ++i)
790
0
        if (write_string(fp, poHeader->papszVariables[i], 32) == 0)
791
0
            return 0;
792
0
    anTemp[0] = poHeader->anUnused[1];
793
0
    anTemp[1] = poHeader->nEpsg;
794
0
    anTemp[2] = (int)poHeader->adfOrigin[0];
795
0
    anTemp[3] = (int)poHeader->adfOrigin[1];
796
0
    for (size_t i = 4; i < 9; ++i)
797
0
        anTemp[i] = poHeader->anUnused[i - 2];
798
0
    anTemp[9] = (poHeader->panStartDate != nullptr) ? 1 : 0;
799
0
    if (write_intarray(fp, anTemp, 10) == 0)
800
0
        return 0;
801
0
    if (poHeader->panStartDate != nullptr &&
802
0
        write_intarray(fp, poHeader->panStartDate, 6) == 0)
803
0
        return 0;
804
0
    anTemp[0] = poHeader->nElements;
805
0
    anTemp[1] = poHeader->nPoints;
806
0
    anTemp[2] = poHeader->nPointsPerElement;
807
0
    anTemp[3] = 1;
808
0
    if (write_intarray(fp, anTemp, 4) == 0)
809
0
        return 0;
810
0
    if (write_intarray(fp, poHeader->panConnectivity,
811
0
                       static_cast<size_t>(poHeader->nElements) *
812
0
                           poHeader->nPointsPerElement) == 0)
813
0
        return 0;
814
0
    if (write_intarray(fp, poHeader->panBorder, poHeader->nPoints) == 0)
815
0
        return 0;
816
0
    double *dfVals =
817
0
        (double *)VSI_MALLOC2_VERBOSE(sizeof(double), poHeader->nPoints);
818
0
    if (poHeader->nPoints > 0 && dfVals == nullptr)
819
0
        return 0;
820
0
    for (size_t i = 0; i < 2; ++i)
821
0
    {
822
0
        for (int j = 0; j < poHeader->nPoints; ++j)
823
0
            dfVals[j] = poHeader->paadfCoords[i][j] - poHeader->adfOrigin[i];
824
0
        if (write_floatarray(fp, dfVals, poHeader->nPoints) == 0)
825
0
        {
826
0
            CPLFree(dfVals);
827
0
            return 0;
828
0
        }
829
0
    }
830
0
    CPLFree(dfVals);
831
0
    return 1;
832
0
}
833
834
#ifdef notdef
835
int read_step(VSILFILE *fp, const Header *poHeader, TimeStep *&poStep)
836
{
837
    poStep = new TimeStep(poHeader->nPoints, poHeader->nVar);
838
    int nLength = 0;
839
    if (read_integer(fp, nLength) == 0 || nLength != 1)
840
    {
841
        delete poStep;
842
        return 0;
843
    }
844
    if (read_float(fp, poStep->dfDate) == 0)
845
    {
846
        delete poStep;
847
        return 0;
848
    }
849
    if (read_integer(fp, nLength) == 0 || nLength != 1)
850
    {
851
        delete poStep;
852
        return 0;
853
    }
854
    for (int i = 0; i < poHeader->nVar; ++i)
855
    {
856
        nLength = read_floatarray(fp, &(poStep->papadfData[i]));
857
        if (nLength != poHeader->nPoints)
858
        {
859
            delete poStep;
860
            return 0;
861
        }
862
    }
863
    return 1;
864
}
865
866
int write_step(VSILFILE *fp, const Header *poHeader, const TimeStep *poStep)
867
{
868
    if (write_integer(fp, 1) == 0)
869
        return 0;
870
    if (write_float(fp, poStep->dfDate) == 0)
871
        return 0;
872
    if (write_integer(fp, 1) == 0)
873
        return 0;
874
    for (int i = 0; i < poHeader->nVar; ++i)
875
    {
876
        if (write_floatarray(fp, poStep->papadfData[i]) == 0)
877
            return 0;
878
    }
879
    return 1;
880
}
881
882
int read_steps(VSILFILE *fp, const Header *poHeader, TimeStepList *&poSteps)
883
{
884
    poSteps = 0;
885
    TimeStepList *poCur, *poNew;
886
    for (int i = 0; i < poHeader->nSteps; ++i)
887
    {
888
        poNew = new TimeStepList(0, 0);
889
        if (read_step(fp, poHeader, poNew->poStep) == 0)
890
        {
891
            delete poSteps;
892
            return 0;
893
        }
894
        if (poSteps == 0)
895
            poSteps = poNew;
896
        else
897
            poCur->poNext = poNew;
898
        poCur = poNew;
899
    }
900
    return 1;
901
}
902
903
int write_steps(VSILFILE *fp, const Header *poHeader,
904
                const TimeStepList *poSteps)
905
{
906
    const TimeStepList *poCur = poSteps;
907
    while (poCur != 0)
908
    {
909
        if (write_step(fp, poHeader, poCur->poStep) == 0)
910
            return 0;
911
        poCur = poCur->poNext;
912
    }
913
    return 1;
914
}
915
#endif
916
}  // namespace Selafin