summaryrefslogtreecommitdiff
path: root/svl/source/filerec/filerec.cxx
blob: d1f93344511ba716376e05169de102333c3b4f40 (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
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
/**************************************************************
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_svl.hxx"
#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;
    }
#ifdef DBG_UTIL
    // mu\s Fix-Size-Record gepr"uft werden?
    else if ( SFX_BOOL_DONTCARE == _bHeaderOk )
    {
        // Header auslesen, um Soll-Gr"o\se zu bestimmen
        sal_uInt32 nEndPos = _pStream->Tell();
        _pStream->Seek( _nStartPos );
        sal_uInt32 nHeader;
        *_pStream >> nHeader;
        _pStream->Seek( nEndPos );

        // Soll-Gr"o\se mit Ist-Gr"o\se vergleichen
        DBG_ASSERT( nEndPos - SFX_REC_OFS(nHeader) == _nStartPos + sizeof(sal_uInt32),
                    "fixed record size incorrect" );
        DbgOutf( "SfxFileRec: written record until %ul", nEndPos );
    }
#endif

    // Record war bereits geschlossen
    return 0;
}

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

sal_uInt16 SfxMiniRecordReader::ScanRecordType
(
    SvStream*   pStream         /*  <SvStream> an dessen aktueller Position
                                    ein Record liegt, dessen Typ erkannt werden
                                    soll.
                                */
)

/*  [Beschreibung]

    Mit dieser statischen Methode kann ermittelt werden, ob sich an der
    aktuellen Position in einem Stream ein Record befindet, und der Typ
    des Records kann ermittelt werden.

    Die Position im Stream ist nach dem Aufruf aufver"andert.


    [Anmerkung]

    Die Record-Typen k"onnen zwar (abgesehen vom Drawing-Enginge-Record)
    untereinander eindeutig erkannt werden, es besteht jedoch die Gefahr
    der Verwechslung von Records mit normalen Daten. File-Formate sollten
    darauf R"ucksicht nehmen. Handelt es sich um keinen Record, wird
    am wahrscheinlichsten SFX_REC_TYPE_MINI zur"uckgeliefert, da dieser
    Typ sich aufgrund seines sparsam kurzen Headers durch die k"urzeste
    Kennung auszeichnet.


    [R"uckgabewert]

    sal_uInt16                          SFX_REC_TYPE_EOR
                                    An der aktuellen Position des Streams
                                    steht eine End-Of-Records-Kennung.

                                    SFX_REC_TYPE_MINI
                                    Es handelt sich um einen SW3 kompatiblen
                                    Mini-Record, dessen einzige Kennung sein
                                    'Mini-Tag' ist.

                                    SFX_REC_TYPE_SINGLE
                                    Es handelt sich um einen Extended-Record
                                    mit einem einzigen Content, der durch eine
                                    Version und ein Tag n"aher gekennzeichnet
                                    ist.

                                    SFX_REC_TYPE_FIXSIZE
                                    Es handelt sich um einen Extended-Record
                                    mit mehreren Contents gleicher Gr"o\se,
                                    die gemeinsam durch eine einzige Version
                                    und ein einziges gemeinsames Tag n"aher
                                    gekennzeichnet sind.

                                    SFX_REC_TYPE_VARSIZE
                                    Es handelt sich um einen Extended-Record
                                    mit mehreren Contents variabler Gr"o\se,
                                    die gemeinsam durch eine einzige Version
                                    und ein einziges gemeinsames Tag n"aher
                                    gekennzeichnet sind.

                                    SFX_REC_TYPE_MIXTAGS
                                    Es handelt sich um einen Extended-Record
                                    mit mehreren Contents variabler Gr"o\se,
                                    die jeweils durch ein eignes Tag und
                                    eine eigene Versions-Nummer n"aher
                                    gekennzeichnet sind.

                                    SFX_REC_TYPE_DRAWENG
                                    Es handelt sich wahrscheinlich um einen
                                    Drawing-Engine-Record. Dieser Record-Typ
                                    kann von den Klassen dieser Gruppe nicht
                                    interpretiert werden.
*/

{
    // die ersten 4 Bytes als Mini-Header lesen
    sal_uInt32 nHeader;
    *pStream >> nHeader;

    // k"onnte es sich um einen extended-Record handeln?
    sal_uInt16 nPreTag = sal::static_int_cast< sal_uInt16 >(SFX_REC_PRE(nHeader));
    if ( SFX_REC_PRETAG_EXT == nPreTag )
    {
        // die n"achsten 4 Bytes als extended-Header lesen
        *pStream >> nHeader;

        // Stream-Position restaurieren
        pStream->SeekRel(-8);

        // liegt eine g"ultige Record-Kennung vor?
        sal_uInt16 nType = sal::static_int_cast< sal_uInt16 >(SFX_REC_TYP(nHeader));
        if ( nType >= SFX_REC_TYPE_FIRST && nType <= SFX_REC_TYPE_LAST )
            // entsprechenden extended-Record-Typ zur"uckliefern
            return nType;

        // sonst ist der Record-Typ unbekannt
        return SFX_REC_TYPE_NONE;
    }

    // Stream-Position restaurieren
    pStream->SeekRel(-4);

    // liegt eine End-Of-Record-Kennung vor?
    if ( SFX_REC_PRETAG_EOR == nPreTag )
        return nPreTag;

    // liegt ein Drawin-Engine-Record vor?
    if ( nHeader == sal_uInt32(*"DRMD") || nHeader == sal_uInt32(*"DRVW") )
        return SFX_REC_TYPE_DRAWENG;

    // alle anderen sind grunds"atzlich g"ultige Mini-Records
    return SFX_REC_TYPE_MINI;
}

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

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 = false;
    }
    return bRet;
}

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

SfxMiniRecordReader::SfxMiniRecordReader
(
    SvStream*       pStream         /*  <SvStream>, an dessen aktueller
                                        Position sich ein <SfxMiniRecord>
                                        befindet.
                                    */
)

/*  [Beschreibung]

    Dieser Ctor liest den Header eines <SfxMiniRecord> ab der aktuellen
    Position von 'pStream'. Da grunds"atzlich fast 4-Byte Kombination ein
    g"ultiger SfxMiniRecord-Header ist, bleiben die einzig m"oglichen
    Fehler der EOF-Status des Streams, und ein SFX_REC_PRETAG_EOR
    als Pre-Tag. Ein entsprechender Error-Code (ERRCODE_IO_EOF bzw.
    ERRCODE_IO_WRONGFORMAT) ist dann am Stream gesetzt, dessen Position
    dann au\serdem unver"andert ist.
*/

:   _pStream( pStream ),
    _bSkipped( false )
{
    // Header einlesen
    sal_uInt32 nStartPos = pStream->Tell(); // um im Fehlerfall zur"uck zu-seeken
    DBG( DbgOutf( "SfxFileRec: reading record at %ul", nStartPos ) );
    sal_uInt32 nHeader;
    *pStream >> nHeader;

    // Headerdaten extrahieren
    SetHeader_Impl( nHeader );

    // Fehlerbehandlung
    if ( pStream->IsEof() )
        _nPreTag = SFX_REC_PRETAG_EOR;
    else if ( _nPreTag == SFX_REC_PRETAG_EOR )
        pStream->SetError( ERRCODE_IO_WRONGFORMAT );
    if ( !IsValid() )
        pStream->Seek( nStartPos );
}

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

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);
}

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

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

/*  [Beschreibung]

    Legt in 'pStream' einen 'SfxSingleRecord' an, dessen Content-Gr"o\se
    nicht bekannt ist, sondern nach dam Streamen des Contents errechnet
    werden soll.
*/

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

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

SfxSingleRecordWriter::SfxSingleRecordWriter
(
    SvStream*       pStream,        // Stream, in dem der Record angelegt wird
    sal_uInt16          nContentTag,    // Inhalts-Art-Kennung
    sal_uInt8           nContentVer,    // Inhalts-Versions-Kennung
    sal_uInt32          nContentSize    // Gr"o\se des Inhalts in Bytes
)

/*  [Beschreibung]

    Legt in 'pStream' einen 'SfxSingleRecord' an, dessen Content-Gr"o\se
    von vornherein bekannt ist.
*/

:   SfxMiniRecordWriter( pStream, SFX_REC_PRETAG_EXT,
                         nContentSize + SFX_REC_HEADERSIZE_SINGLE )
{
    // Erweiterten Header hinter den des SfxMiniRec schreiben
    *pStream << SFX_REC_HEADER( SFX_REC_TYPE_SINGLE, 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;
}

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

SfxSingleRecordReader::SfxSingleRecordReader( SvStream *pStream )
:   SfxMiniRecordReader()
{
    // Startposition merken, um im Fehlerfall zur"uck-seeken zu k"onnen
    #ifdef DBG_UTIL
    sal_uInt32 nStartPos = pStream->Tell();
    DBG( DbgOutf( "SfxFileRec: reading record at %ul", nStartPos ) );
    #endif

    // Basisklasse initialisieren (nicht via Ctor, da der nur MiniRecs akzept.)
    Construct_Impl( pStream );

    // nur Header mit korrektem Record-Type akzeptieren
    if ( !ReadHeader_Impl( SFX_REC_TYPE_SINGLE ) )
    {
        // Error-Code setzen und zur"uck-seeken
        pStream->SeekRel( - SFX_REC_HEADERSIZE_SINGLE );
        pStream->SetError( ERRCODE_IO_WRONGFORMAT );
    }
}

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

SfxSingleRecordReader::SfxSingleRecordReader( SvStream *pStream, sal_uInt16 nTag )
{
    // StartPos merken, um im Fehlerfall zur"uck-seeken zu k"onnen
    sal_uInt32 nStartPos = pStream->Tell();

    // richtigen Record suchen, ggf. Error-Code setzen und zur"uck-seeken
    Construct_Impl( pStream );
    if ( !FindHeader_Impl( SFX_REC_TYPE_SINGLE, nTag ) )
    {
        // Error-Code setzen und zur"uck-seeken
        pStream->Seek( nStartPos );
        pStream->SetError( ERRCODE_IO_WRONGFORMAT );
    }
}

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

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 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 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
    sal_uInt32                          // Gr"o\se jedes einzelnen Contents in Bytes
)

/*  [Beschreibung]

    Interne Methode f"ur Subklassen.
*/

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

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

SfxMultiFixRecordWriter::SfxMultiFixRecordWriter
(
    SvStream*       pStream,        // Stream, in dem der Record angelegt wird
    sal_uInt16          nContentTag,    // Content-Art-Kennung
    sal_uInt8           nContentVer,    // Content-Versions-Kennung
    sal_uInt32                          // Gr"o\se jedes einzelnen Contents in Bytes
)

/*  [Beschreibung]

    Legt in 'pStream' einen 'SfxMultiFixRecord' an, dessen Content-Gr"o\se
    konstant und von vornherein bekannt ist.
*/

:   SfxSingleRecordWriter( SFX_REC_TYPE_FIXSIZE,
                           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, 0 ),
    _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, 0 ),
    _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 )
    : _pContentOfs(0)
    , _nContentSize(0)
    , _nContentCount(0)
    , _nContentNo(0)
{
    // Position im Stream merken, um im Fehlerfall zur"uck-seeken zu k"onnen
    _nStartPos = pStream->Tell();

    // Basisklasse konstruieren (normaler Ctor w"urde nur SingleRecs lesen)
    SfxSingleRecordReader::Construct_Impl( pStream );

    // Header der Basisklasse lesen
    if ( !SfxSingleRecordReader::ReadHeader_Impl( SFX_REC_TYPE_FIXSIZE |
                SFX_REC_TYPE_VARSIZE | SFX_REC_TYPE_VARSIZE_RELOC |
                SFX_REC_TYPE_MIXTAGS | SFX_REC_TYPE_MIXTAGS_RELOC ) ||
         !ReadHeader_Impl() )
        // als ung"ultig markieren und zur"uck-seeken
        SetInvalid_Impl( _nStartPos );
}

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

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 true;
    }

    return false;
}