summaryrefslogtreecommitdiff
path: root/svl/source/filerec/filerec.cxx
blob: b2ebbd0f8b046ca2b8ed118630e59d806df24259 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#include <svl/filerec.hxx>
#include <osl/endian.h>

//========================================================================

SV_IMPL_VARARR( SfxUINT32s, sal_uInt32 );

//========================================================================

/*  Die folgenden Makros extrahieren Teilbereiche aus einem sal_uInt32 Wert.
    Diese sal_uInt32-Werte werden anstelle der einzelnen Werte gestreamt,
    um Calls zu sparen.
*/

#define SFX_REC_PRE(n) ( ((n) & 0x000000FF) )
#define SFX_REC_OFS(n) ( ((n) & 0xFFFFFF00) >> 8 )
#define SFX_REC_TYP(n) ( ((n) & 0x000000FF) )
#define SFX_REC_VER(n) ( ((n) & 0x0000FF00) >> 8 )
#define SFX_REC_TAG(n) ( ((n) & 0xFFFF0000) >> 16 )

#define SFX_REC_CONTENT_VER(n) ( ((n) & 0x000000FF) )
#define SFX_REC_CONTENT_OFS(n) ( ((n) & 0xFFFFFF00) >> 8 )

//-------------------------------------------------------------------------

/*  Die folgenden Makros setzen Teilbereiche zu einem sal_uInt32 Wert zusammen.
    Diese sal_uInt32-Werte werden anstelle der einzelnen Werte gestreamt,
    um Calls zu sparen.
*/

#define SFX_REC_MINI_HEADER(nPreTag,nStartPos,nEndPos) \
                    ( sal_uInt32(nPreTag) | \
                      sal_uInt32(nEndPos-nStartPos-SFX_REC_HEADERSIZE_MINI) << 8 )

#define SFX_REC_HEADER(nRecType,nContentTag,nContentVer) \
                    ( sal_uInt32(nRecType) | \
                      ( sal_uInt32(nContentVer) << 8 ) | \
                      ( sal_uInt32(nContentTag) << 16 ) )

#define SFX_REC_CONTENT_HEADER(nContentVer,n1StStartPos,nCurStartPos) \
                    ( sal_uInt32(nContentVer) | \
                      sal_uInt32( nCurStartPos - n1StStartPos ) << 8 )

//=========================================================================

sal_uInt32 SfxMiniRecordWriter::Close
(
    bool         bSeekToEndOfRec    /*  true (default)
                                        Der Stream wird an das Ende des Records
                                        positioniert.

                                        false
                                        Der Stream wird an den Anfang des
                                        Contents (also hinter den Header)
                                        positioniert.
                                    */
)

/*  [Beschreibung]

    Diese Methode schlie\st den Record. Dabei wird haupts"achlich der
    Header geschrieben.

    Wurde der Header bereits geschrieben, hat der Aufruf keine Wirkung.


    [R"uckgabewert]

    sal_uInt32      != 0
                Position im Stream, die direkt hinter dem Record liegt.
                'bSeekToEndOfRecord==sal_True'
                => R"uckgabewert == aktuelle Stream-Position nach Aufruf

                == 0
                Der Header war bereits geschrieben worden.
*/

{
    // wurde der Header noch nicht geschrieben?
    if ( !_bHeaderOk )
    {
        // Header an den Anfang des Records schreiben
        sal_uInt32 nEndPos = _pStream->Tell();
        _pStream->Seek( _nStartPos );
        *_pStream << SFX_REC_MINI_HEADER( _nPreTag, _nStartPos, nEndPos );

        // je nachdem ans Ende des Records seeken oder hinter Header bleiben
        if ( bSeekToEndOfRec )
            _pStream->Seek( nEndPos );

        // Header wurde JETZT geschrieben
        _bHeaderOk = true;
        return nEndPos;
    }

    // Record war bereits geschlossen
    return 0;
}

bool SfxMiniRecordReader::SetHeader_Impl( sal_uInt32 nHeader )

/*  [Beschreibung]

    Interne Methode zum nachtr"aglichen Verarbeiten eines extern gelesenen
    Headers. Falls der Header eine End-Of-Records-Kennung darstellt,
    wird am Stream ein Errorcode gesetzt und sal_False zur"uckgeliefert. Im
    Fehlerfall wird der Stream jedoch nicht auf den Record-Anfang zur"uck-
    gesetzt.
*/

{
    bool bRet = true;

    // Record-Ende und Pre-Tag aus dem Header ermitteln
    _nEofRec = _pStream->Tell() + SFX_REC_OFS(nHeader);
    _nPreTag = sal::static_int_cast< sal_uInt8 >(SFX_REC_PRE(nHeader));

    // wenn End-Of-Record-Kennung, dann Fehler
    if ( _nPreTag == SFX_REC_PRETAG_EOR )
    {
        _pStream->SetError( ERRCODE_IO_WRONGFORMAT );
        bRet = true;
    }
    return bRet;
}

SfxMiniRecordReader::SfxMiniRecordReader
(
    SvStream*       pStream,        /*  <SvStream>, an dessen aktueller
                                        Position sich ein <SfxMiniRecord>
                                        befindet.
                                    */
    sal_uInt8           nTag            //  Pre-Tag des gew"unschten Records
)

/*  [Beschreibung]

    Dieser Ctor interpretiert 'pStream' ab der aktuellen Position als
    eine l"uckenlose Folge von, von dieser Klassen-Gruppe interpretierbaren,
    Records. Der in dieser Folge erste als <SfxMiniRecord> interpretierbare
    (also ggf. auch ein extended-Record) mit dem PreTag 'nTag' wird ge"offnet
    und durch diese Instanz repr"asentiert.

    Wird das Ende des Streams oder die Kennung SFX_REC_PRETAG_EOR
    erreicht, bevor ein Record mit dem ge"unschten Pre-Tag gefunden wird,
    ist die erzeugte Instanz ung"ultig ('IsValid() == sal_False'). Ein ent-
    sprechender Error-Code (ERRCODE_IO_EOF bzw. ERRCODE_IO_WRONGFORMAT)
    ist dann am Stream gesetzt, dessen Position ist dann au\serdem unver-
    "andert.

    Bei 'nTag==SFX_FILEREC_PRETAG_EOR' wird nicht versucht, einen Record
    zu lesen, es wird sofort 'IsValid()' auf sal_False gesetzt und kein Error-Code
    am Stream gesetzt. Dies ist dauzu gedacht, ohne 'new' und 'delete'
    abw"rtskompatibel SfxMiniRecords einbauen zu k"onnen. Siehe dazu
    <SfxItemSet::Load()>.


    [Anwendungsvorschlag]

    Wird dieser Ctor in einer bereits ausgelieferten Programmversion
    verwendet, k"onnen in das File-Format jeweils davor kompatibel neue
    Records mit einer anderen Kennung eingef"ugt werden. Diese werden
    schlie\slich automatisch "uberlesen. Erkauft wird diese M"oglichkeit
    allerdings mit etwas schlechterem Laufzeitverhalten im Vergleich mit
    direktem 'drauf-los-lesen', der sich jedoch auf einen Vergleich zweier
    Bytes reduziert, falls der gesuchte Record der erste in der Folge ist.
*/

:   _pStream( pStream ),
    _bSkipped( nTag == SFX_REC_PRETAG_EOR )
{
    // ggf. ignorieren (s.o.)
    if ( _bSkipped )
    {
        _nPreTag = nTag;
        return;
    }

    // StartPos merken, um im Fehlerfall zur"uck-seeken zu k"onnen
    sal_uInt32 nStartPos = pStream->Tell();

    // passenden Record suchen
    while(sal_True)
    {
        // Header lesen
        DBG( DbgOutf( "SfxFileRec: searching record at %ul", pStream->Tell() ) );
        sal_uInt32 nHeader;
        *pStream >> nHeader;

        // Headerdaten von Basisklasse extrahieren lassen
        SetHeader_Impl( nHeader );

        // ggf. Fehler behandeln
        if ( pStream->IsEof() )
            _nPreTag = SFX_REC_PRETAG_EOR;
        else if ( _nPreTag == SFX_REC_PRETAG_EOR )
            pStream->SetError( ERRCODE_IO_WRONGFORMAT );
        else
        {
            // wenn gefunden, dann Schleife abbrechen
            if ( _nPreTag == nTag )
                break;

            // sonst skippen und weitersuchen
            pStream->Seek( _nEofRec );
            continue;
        }

        // Fehler => zur"uck-seeken
        pStream->Seek( nStartPos );
        break;
    }
}

//=========================================================================

SfxSingleRecordWriter::SfxSingleRecordWriter
(
    sal_uInt8           nRecordType,    // f"ur Subklassen
    SvStream*       pStream,        // Stream, in dem der Record angelegt wird
    sal_uInt16          nContentTag,    // Inhalts-Art-Kennung
    sal_uInt8           nContentVer     // Inhalts-Versions-Kennung
)

/*  [Beschreibung]

    Interner Ctor f"ur Subklassen.
*/

:   SfxMiniRecordWriter( pStream, SFX_REC_PRETAG_EXT )
{
    // Erweiterten Header hiner den des SfxMiniRec schreiben
    *pStream << SFX_REC_HEADER(nRecordType, nContentTag, nContentVer);
}

//=========================================================================

inline bool SfxSingleRecordReader::ReadHeader_Impl( sal_uInt16 nTypes )

/*  [Beschreibung]

    Interne Methode zum Einlesen eines SfxMultiRecord-Headers, nachdem
    die Basisklasse bereits initialisiert und deren Header gelesen ist.
    Ggf. ist ein Error-Code am Stream gesetzt, im Fehlerfall wird jedoch
    nicht zur"uckge-seekt.
*/

{
    bool bRet;

    // Basisklassen-Header einlesen
    sal_uInt32 nHeader=0;
    *_pStream >> nHeader;
    if ( !SetHeader_Impl( nHeader ) )
        bRet = false;
    else
    {
        // eigenen Header einlesen
        *_pStream >> nHeader;
        _nRecordVer = sal::static_int_cast< sal_uInt8 >(SFX_REC_VER(nHeader));
        _nRecordTag = sal::static_int_cast< sal_uInt16 >(SFX_REC_TAG(nHeader));

        // falscher Record-Typ?
        _nRecordType = sal::static_int_cast< sal_uInt8 >(SFX_REC_TYP(nHeader));
        bRet = 0 != ( nTypes & _nRecordType);
    }
    return bRet;
}

//-------------------------------------------------------------------------

bool SfxSingleRecordReader::FindHeader_Impl
(
    sal_uInt16      nTypes,     // arithm. Veroderung erlaubter Record-Typen
    sal_uInt16      nTag        // zu findende Record-Art-Kennung
)

/*  [Beschreibung]

    Interne Methode zum lesen des Headers des ersten Record, der einem
    der Typen in 'nTypes' entspricht und mit der Art-Kennung 'nTag'
    gekennzeichnet ist.

    Kann ein solcher Record nicht gefunden werden, wird am Stream ein
    Errorcode gesetzt, zur"uck-geseekt und sal_False zur"uckgeliefert.
*/

{
    // StartPos merken, um im Fehlerfall zur"uck-seeken zu k"onnen
    sal_uInt32 nStartPos = _pStream->Tell();

    // richtigen Record suchen
    while ( !_pStream->IsEof() )
    {
        // Header lesen
        sal_uInt32 nHeader;
        DBG( DbgOutf( "SfxFileRec: searching record at %ul", _pStream->Tell() ) );
        *_pStream >> nHeader;
        if ( !SetHeader_Impl( nHeader ) )
            // EOR => Such-Schleife abbreichen
            break;

        // Extended Record gefunden?
        if ( _nPreTag == SFX_REC_PRETAG_EXT )
        {
            // Extended Header lesen
            *_pStream >> nHeader;
            _nRecordTag = sal::static_int_cast< sal_uInt16 >(SFX_REC_TAG(nHeader));

            // richtigen Record gefunden?
            if ( _nRecordTag == nTag )
            {
                // gefundener Record-Typ passend?
                _nRecordType = sal::static_int_cast< sal_uInt8 >(
                    SFX_REC_TYP(nHeader));
                if ( nTypes & _nRecordType )
                    // ==> gefunden
                    return sal_True;

                // error => Such-Schleife abbrechen
                break;
            }
        }

        // sonst skippen
        if ( !_pStream->IsEof() )
            _pStream->Seek( _nEofRec );
    }

    // Fehler setzen und zur"uck-seeken
    _pStream->SetError( ERRCODE_IO_WRONGFORMAT );
    _pStream->Seek( nStartPos );
    return sal_False;
}

//=========================================================================

SfxMultiFixRecordWriter::SfxMultiFixRecordWriter
(
    sal_uInt8           nRecordType,    // Subklassen Record-Kennung
    SvStream*       pStream,        // Stream, in dem der Record angelegt wird
    sal_uInt16          nContentTag,    // Content-Art-Kennung
    sal_uInt8           nContentVer     // Content-Versions-Kennung
)

/*  [Beschreibung]

    Interne Methode f"ur Subklassen.
*/

:   SfxSingleRecordWriter( nRecordType, pStream, nContentTag, nContentVer ),
    _nContentCount( 0 )
{
    // Platz f"ur eigenen Header
    pStream->SeekRel( + SFX_REC_HEADERSIZE_MULTI );
}

//------------------------------------------------------------------------

sal_uInt32 SfxMultiFixRecordWriter::Close( bool bSeekToEndOfRec )

//  siehe <SfxMiniRecordWriter>

{
    // Header noch nicht geschrieben?
    if ( !_bHeaderOk )
    {
        // Position hinter Record merken, um sie restaurieren zu k"onnen
        sal_uInt32 nEndPos = SfxSingleRecordWriter::Close( sal_False );

        // gegen"uber SfxSingleRecord erweiterten Header schreiben
        *_pStream << _nContentCount;
        *_pStream << _nContentSize;

        // je nachdem ans Ende des Records seeken oder hinter Header bleiben
        if ( bSeekToEndOfRec )
            _pStream->Seek(nEndPos);
        return nEndPos;
    }

    // Record war bereits geschlossen
    return 0;
}

//=========================================================================

SfxMultiVarRecordWriter::SfxMultiVarRecordWriter
(
    sal_uInt8           nRecordType,    // Record-Kennung der Subklasse
    SvStream*       pStream,        // Stream, in dem der Record angelegt wird
    sal_uInt16          nRecordTag,     // Gesamt-Art-Kennung
    sal_uInt8           nRecordVer      // Gesamt-Versions-Kennung
)

/*  [Beschreibung]

    Interner Ctor f"ur Subklassen.
*/

:   SfxMultiFixRecordWriter( nRecordType, pStream, nRecordTag, nRecordVer ),
    _nContentVer( 0 )
{
}

//-------------------------------------------------------------------------

SfxMultiVarRecordWriter::SfxMultiVarRecordWriter
(
    SvStream*       pStream,        // Stream, in dem der Record angelegt wird
    sal_uInt16          nRecordTag,     // Gesamt-Art-Kennung
    sal_uInt8           nRecordVer      // Gesamt-Versions-Kennung
)

/*  [Beschreibung]

    Legt in 'pStream' einen 'SfxMultiVarRecord' an, dessen Content-Gr"o\sen
    weder bekannt sind noch identisch sein m"ussen, sondern jeweils nach dem
    Streamen jedes einzelnen Contents errechnet werden sollen.


    [Anmerkung]

    Diese Methode ist nicht inline, da f"ur die Initialisierung eines
    <SvULongs>-Members zu viel Code generiert werden w"urde.
*/

:   SfxMultiFixRecordWriter( SFX_REC_TYPE_VARSIZE,
                             pStream, nRecordTag, nRecordVer ),
    _nContentVer( 0 )
{
}

//-------------------------------------------------------------------------

SfxMultiVarRecordWriter::~SfxMultiVarRecordWriter()

/*  [Beschreibung]

    Der Dtor der Klasse <SfxMultiVarRecordWriter> schlie\st den Record
    automatisch, falls <SfxMultiVarRecordWriter::Close()> nicht bereits
    explizit gerufen wurde.
*/

{
    // wurde der Header noch nicht geschrieben oder mu\s er gepr"uft werden
    if ( !_bHeaderOk )
        Close();
}

//-------------------------------------------------------------------------

void SfxMultiVarRecordWriter::FlushContent_Impl()

/*  [Beschreibung]

    Interne Methode zum Abschlie\sen eines einzelnen Contents.
*/

{
    // Versions-Kennung und Positions-Offset des aktuellen Contents merken;
    // das Positions-Offset ist relativ zur Startposition des ersten Contents
    _aContentOfs.Insert(
            SFX_REC_CONTENT_HEADER(_nContentVer,_nStartPos,_nContentStartPos),
            _nContentCount-1 );
}

//-------------------------------------------------------------------------

void SfxMultiVarRecordWriter::NewContent()

// siehe <SfxMultiFixRecordWriter>

{
    // schon ein Content geschrieben?
    if ( _nContentCount )
        FlushContent_Impl();

    // neuen Content beginnen
    _nContentStartPos = _pStream->Tell();
    ++_nContentCount;
}

//-------------------------------------------------------------------------

sal_uInt32 SfxMultiVarRecordWriter::Close( bool bSeekToEndOfRec )

// siehe <SfxMiniRecordWriter>

{
    // Header noch nicht geschrieben?
    if ( !_bHeaderOk )
    {
        // ggf. letzten Content abschlie\sen
        if ( _nContentCount )
            FlushContent_Impl();

        // Content-Offset-Tabelle schreiben
        sal_uInt32 nContentOfsPos = _pStream->Tell();
        //! darf man das so einr"ucken?
        #if defined(OSL_LITENDIAN)
            _pStream->Write( _aContentOfs.GetData(),
                             sizeof(sal_uInt32)*_nContentCount );
        #else
            for ( sal_uInt16 n = 0; n < _nContentCount; ++n )
                *_pStream << sal_uInt32(_aContentOfs[n]);
        #endif

        // SfxMultiFixRecordWriter::Close() "uberspringen!
        sal_uInt32 nEndPos = SfxSingleRecordWriter::Close( sal_False );

        // eigenen Header schreiben
        *_pStream << _nContentCount;
        if ( SFX_REC_TYPE_VARSIZE_RELOC == _nPreTag ||
             SFX_REC_TYPE_MIXTAGS_RELOC == _nPreTag )
            *_pStream << static_cast<sal_uInt32>(nContentOfsPos - ( _pStream->Tell() + sizeof(sal_uInt32) ));
        else
            *_pStream << nContentOfsPos;

        // ans Ende des Records seeken bzw. am Ende des Headers bleiben
        if ( bSeekToEndOfRec )
             _pStream->Seek(nEndPos);
        return nEndPos;
    }

    // Record war bereits vorher geschlossen
    return 0;
}

//=========================================================================

void SfxMultiMixRecordWriter::NewContent
(
    sal_uInt16      nContentTag,    // Kennung f"ur die Art des Contents
    sal_uInt8       nContentVer     // Kennung f"ur die Version des Contents
)

/*  [Beschreibung]

    Mit dieser Methode wird in den Record ein neuer Content eingef"ugt
    und dessen Content-Tag sowie dessen Content-Version angegeben. Jeder,
    auch der 1. Record mu\s durch Aufruf dieser Methode eingeleitet werden.
*/

{
    // ggf. vorherigen Record abschlie\sen
    if ( _nContentCount )
        FlushContent_Impl();

    // Tag vor den Content schreiben, Version und Startposition merken
    _nContentStartPos = _pStream->Tell();
    ++_nContentCount;
    *_pStream << nContentTag;
    _nContentVer = nContentVer;
}

//=========================================================================

bool SfxMultiRecordReader::ReadHeader_Impl()

/*  [Beschreibung]

    Interne Methode zum Einlesen eines SfxMultiRecord-Headers, nachdem
    die Basisklasse bereits initialisiert und deren Header gelesen ist.
    Ggf. ist ein Error-Code am Stream gesetzt, im Fehlerfall wird jedoch
    nicht zur"uckge-seekt.
*/

{
    // eigenen Header lesen
    *_pStream >> _nContentCount;
    *_pStream >> _nContentSize; // Fix: jedes einzelnen, Var|Mix: Tabellen-Pos.

    // mu\s noch eine Tabelle mit Content-Offsets geladen werden?
    if ( _nRecordType != SFX_REC_TYPE_FIXSIZE )
    {
        // Tabelle aus dem Stream einlesen
        sal_uInt32 nContentPos = _pStream->Tell();
        if ( _nRecordType == SFX_REC_TYPE_VARSIZE_RELOC ||
             _nRecordType == SFX_REC_TYPE_MIXTAGS_RELOC )
            _pStream->SeekRel( + _nContentSize );
        else
            _pStream->Seek( _nContentSize );
        _pContentOfs = new sal_uInt32[_nContentCount];
        memset(_pContentOfs, 0, _nContentCount*sizeof(sal_uInt32));
        //! darf man jetzt so einr"ucken
        #if defined(OSL_LITENDIAN)
        _pStream->Read( _pContentOfs, sizeof(sal_uInt32)*_nContentCount );
        #else
        for ( sal_uInt16 n = 0; n < _nContentCount; ++n )
            *_pStream >> _pContentOfs[n];
        #endif
        _pStream->Seek( nContentPos );
    }

    // Header konnte gelesen werden, wenn am Stream kein Error gesetzt ist
    return !_pStream->GetError();
}

//-------------------------------------------------------------------------

SfxMultiRecordReader::SfxMultiRecordReader( SvStream *pStream, sal_uInt16 nTag )
:   _nContentNo(0)
{
    // Position im Stream merken, um im Fehlerfall zur"uck-seeken zu k"onnen
    _nStartPos = pStream->Tell();

    // passenden Record suchen und Basisklasse initialisieren
    SfxSingleRecordReader::Construct_Impl( pStream );
    if ( SfxSingleRecordReader::FindHeader_Impl( SFX_REC_TYPE_FIXSIZE |
            SFX_REC_TYPE_VARSIZE | SFX_REC_TYPE_VARSIZE_RELOC |
            SFX_REC_TYPE_MIXTAGS | SFX_REC_TYPE_MIXTAGS_RELOC,
            nTag ) )
    {
        // eigenen Header dazu-lesen
        if ( !ReadHeader_Impl() )
            // nicht lesbar => als ung"ultig markieren und zur"uck-seeken
            SetInvalid_Impl( _nStartPos);
    }
}

//-------------------------------------------------------------------------

SfxMultiRecordReader::~SfxMultiRecordReader()
{
    delete[] _pContentOfs;
}

//-------------------------------------------------------------------------

bool SfxMultiRecordReader::GetContent()

/*  [Beschreibung]

    Positioniert den Stream an den Anfang des n"chsten bzw. beim 1. Aufruf
    auf den Anfang des ersten Contents im Record und liest ggf. dessen
    Header ein.

    Liegt laut Record-Header kein Content mehr vor, wird sal_False zur"uck-
    gegeben. Trotz einem sal_True-Returnwert kann am Stream ein Fehlercode
    gesetzt sein, z.B. falls er unvorhergesehenerweise (kaputtes File)
    zuende ist.
*/

{
    // noch ein Content vorhanden?
    if ( _nContentNo < _nContentCount )
    {
        // den Stream an den Anfang des Contents positionieren
        sal_uInt32 nOffset = _nRecordType == SFX_REC_TYPE_FIXSIZE
                    ? _nContentNo * _nContentSize
                    : SFX_REC_CONTENT_OFS(_pContentOfs[_nContentNo]);
        sal_uInt32 nNewPos = _nStartPos + nOffset;
        DBG_ASSERT( nNewPos >= _pStream->Tell(), "SfxMultiRecordReader::GetContent() - New position before current, to much data red!" );

        // #99366#: correct stream pos in every case;
        // the if clause was added by MT  a long time ago,
        // maybe to 'repair' other corrupt documents; but this
        // gives errors when writing with 5.1 and reading with current
        // versions, so we decided to remove the if clause (KA-05/17/2002)
        // if ( nNewPos > _pStream->Tell() )
        _pStream->Seek( nNewPos );

        // ggf. Content-Header lesen
        if ( _nRecordType == SFX_REC_TYPE_MIXTAGS ||
             _nRecordType == SFX_REC_TYPE_MIXTAGS_RELOC )
        {
            _nContentVer = sal::static_int_cast< sal_uInt8 >(
                SFX_REC_CONTENT_VER(_pContentOfs[_nContentNo]));
            *_pStream >> _nContentTag;
        }

        // ContentNo weiterz"ahlen
        ++_nContentNo;
        return sal_True;
    }

    return sal_False;
}


/* vim:set shiftwidth=4 softtabstop=4 expandtab: */