xref: /aoo4110/main/svl/source/filerec/filerec.cxx (revision b1cdbd2c)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_svl.hxx"
26 #include <svl/filerec.hxx>
27 #include <osl/endian.h>
28 
29 //========================================================================
30 
31 SV_IMPL_VARARR( SfxUINT32s, sal_uInt32 );
32 
33 //========================================================================
34 
35 /*	Die folgenden Makros extrahieren Teilbereiche aus einem sal_uInt32 Wert.
36 	Diese sal_uInt32-Werte werden anstelle der einzelnen Werte gestreamt,
37 	um Calls zu sparen.
38 */
39 
40 #define SFX_REC_PRE(n) ( ((n) & 0x000000FF) )
41 #define SFX_REC_OFS(n) ( ((n) & 0xFFFFFF00) >> 8 )
42 #define SFX_REC_TYP(n) ( ((n) & 0x000000FF) )
43 #define SFX_REC_VER(n) ( ((n) & 0x0000FF00) >> 8 )
44 #define SFX_REC_TAG(n) ( ((n) & 0xFFFF0000) >> 16 )
45 
46 #define SFX_REC_CONTENT_VER(n) ( ((n) & 0x000000FF) )
47 #define SFX_REC_CONTENT_OFS(n) ( ((n) & 0xFFFFFF00) >> 8 )
48 
49 //-------------------------------------------------------------------------
50 
51 /*	Die folgenden Makros setzen Teilbereiche zu einem sal_uInt32 Wert zusammen.
52 	Diese sal_uInt32-Werte werden anstelle der einzelnen Werte gestreamt,
53 	um Calls zu sparen.
54 */
55 
56 #define SFX_REC_MINI_HEADER(nPreTag,nStartPos,nEndPos) \
57 					( sal_uInt32(nPreTag) | \
58 					  sal_uInt32(nEndPos-nStartPos-SFX_REC_HEADERSIZE_MINI) << 8 )
59 
60 #define SFX_REC_HEADER(nRecType,nContentTag,nContentVer) \
61 					( sal_uInt32(nRecType) | \
62 					  ( sal_uInt32(nContentVer) << 8 ) | \
63 					  ( sal_uInt32(nContentTag) << 16 ) )
64 
65 #define SFX_REC_CONTENT_HEADER(nContentVer,n1StStartPos,nCurStartPos) \
66 					( sal_uInt32(nContentVer) | \
67 					  sal_uInt32( nCurStartPos - n1StStartPos ) << 8 )
68 
69 //=========================================================================
70 
Close(FASTBOOL bSeekToEndOfRec)71 sal_uInt32 SfxMiniRecordWriter::Close
72 (
73 	FASTBOOL	bSeekToEndOfRec 	/*	sal_True (default)
74 										Der Stream wird an das Ende des Records
75 										positioniert.
76 
77 										sal_False
78 										Der Stream wird an den Anfang des
79 										Contents (also hinter den Header)
80 										positioniert.
81 									*/
82 )
83 
84 /*	[Beschreibung]
85 
86 	Diese Methode schlie\st den Record. Dabei wird haupts"achlich der
87 	Header geschrieben.
88 
89 	Wurde der Header bereits geschrieben, hat der Aufruf keine Wirkung.
90 
91 
92 	[R"uckgabewert]
93 
94 	sal_uInt32		!= 0
95 				Position im Stream, die direkt hinter dem Record liegt.
96 				'bSeekToEndOfRecord==sal_True'
97 				=> R"uckgabewert == aktuelle Stream-Position nach Aufruf
98 
99 				== 0
100 				Der Header war bereits geschrieben worden.
101 */
102 
103 {
104 	// wurde der Header noch nicht geschrieben?
105 	if ( !_bHeaderOk )
106 	{
107 		// Header an den Anfang des Records schreiben
108 		sal_uInt32 nEndPos = _pStream->Tell();
109 		_pStream->Seek( _nStartPos );
110 		*_pStream << SFX_REC_MINI_HEADER( _nPreTag, _nStartPos, nEndPos );
111 
112 		// je nachdem ans Ende des Records seeken oder hinter Header bleiben
113 		if ( bSeekToEndOfRec )
114 			_pStream->Seek( nEndPos );
115 
116 		// Header wurde JETZT geschrieben
117 		_bHeaderOk = sal_True;
118 		return nEndPos;
119 	}
120 #ifdef DBG_UTIL
121 	// mu\s Fix-Size-Record gepr"uft werden?
122 	else if ( SFX_BOOL_DONTCARE == _bHeaderOk )
123 	{
124 		// Header auslesen, um Soll-Gr"o\se zu bestimmen
125 		sal_uInt32 nEndPos = _pStream->Tell();
126 		_pStream->Seek( _nStartPos );
127 		sal_uInt32 nHeader;
128 		*_pStream >> nHeader;
129 		_pStream->Seek( nEndPos );
130 
131 		// Soll-Gr"o\se mit Ist-Gr"o\se vergleichen
132 		DBG_ASSERT( nEndPos - SFX_REC_OFS(nHeader) == _nStartPos + sizeof(sal_uInt32),
133 					"fixed record size incorrect" );
134 		DbgOutf( "SfxFileRec: written record until %ul", nEndPos );
135 	}
136 #endif
137 
138 	// Record war bereits geschlossen
139 	return 0;
140 }
141 
142 //=========================================================================
143 
ScanRecordType(SvStream * pStream)144 sal_uInt16 SfxMiniRecordReader::ScanRecordType
145 (
146 	SvStream*	pStream 		/*	<SvStream> an dessen aktueller Position
147 									ein Record liegt, dessen Typ erkannt werden
148 									soll.
149 								*/
150 )
151 
152 /*	[Beschreibung]
153 
154 	Mit dieser statischen Methode kann ermittelt werden, ob sich an der
155 	aktuellen Position in einem Stream ein Record befindet, und der Typ
156 	des Records kann ermittelt werden.
157 
158 	Die Position im Stream ist nach dem Aufruf aufver"andert.
159 
160 
161 	[Anmerkung]
162 
163 	Die Record-Typen k"onnen zwar (abgesehen vom Drawing-Enginge-Record)
164 	untereinander eindeutig erkannt werden, es besteht jedoch die Gefahr
165 	der Verwechslung von Records mit normalen Daten. File-Formate sollten
166 	darauf R"ucksicht nehmen. Handelt es sich um keinen Record, wird
167 	am wahrscheinlichsten SFX_REC_TYPE_MINI zur"uckgeliefert, da dieser
168 	Typ sich aufgrund seines sparsam kurzen Headers durch die k"urzeste
169 	Kennung auszeichnet.
170 
171 
172 	[R"uckgabewert]
173 
174 	sal_uInt16							SFX_REC_TYPE_EOR
175 									An der aktuellen Position des Streams
176 									steht eine End-Of-Records-Kennung.
177 
178 									SFX_REC_TYPE_MINI
179 									Es handelt sich um einen SW3 kompatiblen
180 									Mini-Record, dessen einzige Kennung sein
181 									'Mini-Tag' ist.
182 
183 									SFX_REC_TYPE_SINGLE
184 									Es handelt sich um einen Extended-Record
185 									mit einem einzigen Content, der durch eine
186 									Version und ein Tag n"aher gekennzeichnet
187 									ist.
188 
189 									SFX_REC_TYPE_FIXSIZE
190 									Es handelt sich um einen Extended-Record
191 									mit mehreren Contents gleicher Gr"o\se,
192 									die gemeinsam durch eine einzige Version
193 									und ein einziges gemeinsames Tag n"aher
194 									gekennzeichnet sind.
195 
196 									SFX_REC_TYPE_VARSIZE
197 									Es handelt sich um einen Extended-Record
198 									mit mehreren Contents variabler Gr"o\se,
199 									die gemeinsam durch eine einzige Version
200 									und ein einziges gemeinsames Tag n"aher
201 									gekennzeichnet sind.
202 
203 									SFX_REC_TYPE_MIXTAGS
204 									Es handelt sich um einen Extended-Record
205 									mit mehreren Contents variabler Gr"o\se,
206 									die jeweils durch ein eignes Tag und
207 									eine eigene Versions-Nummer n"aher
208 									gekennzeichnet sind.
209 
210 									SFX_REC_TYPE_DRAWENG
211 									Es handelt sich wahrscheinlich um einen
212 									Drawing-Engine-Record. Dieser Record-Typ
213 									kann von den Klassen dieser Gruppe nicht
214 									interpretiert werden.
215 */
216 
217 {
218 	// die ersten 4 Bytes als Mini-Header lesen
219 	sal_uInt32 nHeader;
220 	*pStream >> nHeader;
221 
222 	// k"onnte es sich um einen extended-Record handeln?
223 	sal_uInt16 nPreTag = sal::static_int_cast< sal_uInt16 >(SFX_REC_PRE(nHeader));
224 	if ( SFX_REC_PRETAG_EXT == nPreTag )
225 	{
226 		// die n"achsten 4 Bytes als extended-Header lesen
227 		*pStream >> nHeader;
228 
229 		// Stream-Position restaurieren
230 		pStream->SeekRel(-8);
231 
232 		// liegt eine g"ultige Record-Kennung vor?
233 		sal_uInt16 nType = sal::static_int_cast< sal_uInt16 >(SFX_REC_TYP(nHeader));
234 		if ( nType >= SFX_REC_TYPE_FIRST && nType <= SFX_REC_TYPE_LAST )
235 			// entsprechenden extended-Record-Typ zur"uckliefern
236 			return nType;
237 
238 		// sonst ist der Record-Typ unbekannt
239 		return SFX_REC_TYPE_NONE;
240 	}
241 
242 	// Stream-Position restaurieren
243 	pStream->SeekRel(-4);
244 
245 	// liegt eine End-Of-Record-Kennung vor?
246 	if ( SFX_REC_PRETAG_EOR == nPreTag )
247 		return nPreTag;
248 
249 	// liegt ein Drawin-Engine-Record vor?
250 	if ( nHeader == sal_uInt32(*"DRMD") || nHeader == sal_uInt32(*"DRVW") )
251 		return SFX_REC_TYPE_DRAWENG;
252 
253 	// alle anderen sind grunds"atzlich g"ultige Mini-Records
254 	return SFX_REC_TYPE_MINI;
255 }
256 
257 //-------------------------------------------------------------------------
258 
SetHeader_Impl(sal_uInt32 nHeader)259 FASTBOOL SfxMiniRecordReader::SetHeader_Impl( sal_uInt32 nHeader )
260 
261 /*	[Beschreibung]
262 
263 	Interne Methode zum nachtr"aglichen Verarbeiten eines extern gelesenen
264 	Headers. Falls der Header eine End-Of-Records-Kennung darstellt,
265 	wird am Stream ein Errorcode gesetzt und sal_False zur"uckgeliefert. Im
266 	Fehlerfall wird der Stream jedoch nicht auf den Record-Anfang zur"uck-
267 	gesetzt.
268 */
269 
270 {
271 	FASTBOOL bRet = sal_True;
272 
273 	// Record-Ende und Pre-Tag aus dem Header ermitteln
274 	_nEofRec = _pStream->Tell() + SFX_REC_OFS(nHeader);
275 	_nPreTag = sal::static_int_cast< sal_uInt8 >(SFX_REC_PRE(nHeader));
276 
277 	// wenn End-Of-Record-Kennung, dann Fehler
278 	if ( _nPreTag == SFX_REC_PRETAG_EOR )
279 	{
280 		_pStream->SetError( ERRCODE_IO_WRONGFORMAT );
281 		bRet = sal_False;
282 	}
283 	return bRet;
284 }
285 
286 //-------------------------------------------------------------------------
287 
SfxMiniRecordReader(SvStream * pStream)288 SfxMiniRecordReader::SfxMiniRecordReader
289 (
290 	SvStream*		pStream 		/*	<SvStream>, an dessen aktueller
291 										Position sich ein <SfxMiniRecord>
292 										befindet.
293 									*/
294 )
295 
296 /*	[Beschreibung]
297 
298 	Dieser Ctor liest den Header eines <SfxMiniRecord> ab der aktuellen
299 	Position von 'pStream'. Da grunds"atzlich fast 4-Byte Kombination ein
300 	g"ultiger SfxMiniRecord-Header ist, bleiben die einzig m"oglichen
301 	Fehler der EOF-Status des Streams, und ein SFX_REC_PRETAG_EOR
302 	als Pre-Tag. Ein entsprechender Error-Code (ERRCODE_IO_EOF bzw.
303 	ERRCODE_IO_WRONGFORMAT) ist dann am Stream gesetzt, dessen Position
304 	dann au\serdem unver"andert ist.
305 */
306 
307 :	_pStream( pStream ),
308 	_bSkipped( sal_False )
309 {
310 	// Header einlesen
311 	sal_uInt32 nStartPos = pStream->Tell(); // um im Fehlerfall zur"uck zu-seeken
312 	DBG( DbgOutf( "SfxFileRec: reading record at %ul", nStartPos ) );
313 	sal_uInt32 nHeader;
314 	*pStream >> nHeader;
315 
316 	// Headerdaten extrahieren
317 	SetHeader_Impl( nHeader );
318 
319 	// Fehlerbehandlung
320 	if ( pStream->IsEof() )
321 		_nPreTag = SFX_REC_PRETAG_EOR;
322 	else if ( _nPreTag == SFX_REC_PRETAG_EOR )
323 		pStream->SetError( ERRCODE_IO_WRONGFORMAT );
324 	if ( !IsValid() )
325 		pStream->Seek( nStartPos );
326 }
327 
328 //-------------------------------------------------------------------------
329 
SfxMiniRecordReader(SvStream * pStream,sal_uInt8 nTag)330 SfxMiniRecordReader::SfxMiniRecordReader
331 (
332 	SvStream*		pStream,		/*	<SvStream>, an dessen aktueller
333 										Position sich ein <SfxMiniRecord>
334 										befindet.
335 									*/
336 	sal_uInt8			nTag			//	Pre-Tag des gew"unschten Records
337 )
338 
339 /*	[Beschreibung]
340 
341 	Dieser Ctor interpretiert 'pStream' ab der aktuellen Position als
342 	eine l"uckenlose Folge von, von dieser Klassen-Gruppe interpretierbaren,
343 	Records. Der in dieser Folge erste als <SfxMiniRecord> interpretierbare
344 	(also ggf. auch ein extended-Record) mit dem PreTag 'nTag' wird ge"offnet
345 	und durch diese Instanz repr"asentiert.
346 
347 	Wird das Ende des Streams oder die Kennung SFX_REC_PRETAG_EOR
348 	erreicht, bevor ein Record mit dem ge"unschten Pre-Tag gefunden wird,
349 	ist die erzeugte Instanz ung"ultig ('IsValid() == sal_False'). Ein ent-
350 	sprechender Error-Code (ERRCODE_IO_EOF bzw. ERRCODE_IO_WRONGFORMAT)
351 	ist dann am Stream gesetzt, dessen Position ist dann au\serdem unver-
352 	"andert.
353 
354 	Bei 'nTag==SFX_FILEREC_PRETAG_EOR' wird nicht versucht, einen Record
355 	zu lesen, es wird sofort 'IsValid()' auf sal_False gesetzt und kein Error-Code
356 	am Stream gesetzt. Dies ist dauzu gedacht, ohne 'new' und 'delete'
357 	abw"rtskompatibel SfxMiniRecords einbauen zu k"onnen. Siehe dazu
358 	<SfxItemSet::Load()>.
359 
360 
361 	[Anwendungsvorschlag]
362 
363 	Wird dieser Ctor in einer bereits ausgelieferten Programmversion
364 	verwendet, k"onnen in das File-Format jeweils davor kompatibel neue
365 	Records mit einer anderen Kennung eingef"ugt werden. Diese werden
366 	schlie\slich automatisch "uberlesen. Erkauft wird diese M"oglichkeit
367 	allerdings mit etwas schlechterem Laufzeitverhalten im Vergleich mit
368 	direktem 'drauf-los-lesen', der sich jedoch auf einen Vergleich zweier
369 	Bytes reduziert, falls der gesuchte Record der erste in der Folge ist.
370 */
371 
372 :   _pStream( pStream ),
373 	_bSkipped( nTag == SFX_REC_PRETAG_EOR )
374 {
375 	// ggf. ignorieren (s.o.)
376 	if ( _bSkipped )
377 	{
378 		_nPreTag = nTag;
379 		return;
380 	}
381 
382 	// StartPos merken, um im Fehlerfall zur"uck-seeken zu k"onnen
383 	sal_uInt32 nStartPos = pStream->Tell();
384 
385 	// passenden Record suchen
386 	while(sal_True)
387 	{
388 		// Header lesen
389 		DBG( DbgOutf( "SfxFileRec: searching record at %ul", pStream->Tell() ) );
390 		sal_uInt32 nHeader;
391 		*pStream >> nHeader;
392 
393 		// Headerdaten von Basisklasse extrahieren lassen
394 		SetHeader_Impl( nHeader );
395 
396 		// ggf. Fehler behandeln
397 		if ( pStream->IsEof() )
398 			_nPreTag = SFX_REC_PRETAG_EOR;
399 		else if ( _nPreTag == SFX_REC_PRETAG_EOR )
400 			pStream->SetError( ERRCODE_IO_WRONGFORMAT );
401 		else
402 		{
403 			// wenn gefunden, dann Schleife abbrechen
404 			if ( _nPreTag == nTag )
405 				break;
406 
407 			// sonst skippen und weitersuchen
408 			pStream->Seek( _nEofRec );
409 			continue;
410 		}
411 
412 		// Fehler => zur"uck-seeken
413 		pStream->Seek( nStartPos );
414 		break;
415 	}
416 }
417 
418 //=========================================================================
419 
SfxSingleRecordWriter(sal_uInt8 nRecordType,SvStream * pStream,sal_uInt16 nContentTag,sal_uInt8 nContentVer)420 SfxSingleRecordWriter::SfxSingleRecordWriter
421 (
422 	sal_uInt8			nRecordType,	// f"ur Subklassen
423 	SvStream*		pStream,		// Stream, in dem der Record angelegt wird
424 	sal_uInt16			nContentTag,	// Inhalts-Art-Kennung
425 	sal_uInt8			nContentVer 	// Inhalts-Versions-Kennung
426 )
427 
428 /*	[Beschreibung]
429 
430 	Interner Ctor f"ur Subklassen.
431 */
432 
433 :	SfxMiniRecordWriter( pStream, SFX_REC_PRETAG_EXT )
434 {
435 	// Erweiterten Header hiner den des SfxMiniRec schreiben
436 	*pStream << SFX_REC_HEADER(nRecordType, nContentTag, nContentVer);
437 }
438 
439 //-------------------------------------------------------------------------
440 
SfxSingleRecordWriter(SvStream * pStream,sal_uInt16 nContentTag,sal_uInt8 nContentVer)441 SfxSingleRecordWriter::SfxSingleRecordWriter
442 (
443 	SvStream*		pStream,		// Stream, in dem der Record angelegt wird
444 	sal_uInt16			nContentTag,	// Inhalts-Art-Kennung
445 	sal_uInt8			nContentVer 	// Inhalts-Versions-Kennung
446 )
447 
448 /*	[Beschreibung]
449 
450 	Legt in 'pStream' einen 'SfxSingleRecord' an, dessen Content-Gr"o\se
451 	nicht bekannt ist, sondern nach dam Streamen des Contents errechnet
452 	werden soll.
453 */
454 
455 :	SfxMiniRecordWriter( pStream, SFX_REC_PRETAG_EXT )
456 {
457 	// Erweiterten Header hiner den des SfxMiniRec schreiben
458 	*pStream << SFX_REC_HEADER( SFX_REC_TYPE_SINGLE, nContentTag, nContentVer);
459 }
460 
461 //-------------------------------------------------------------------------
462 
SfxSingleRecordWriter(SvStream * pStream,sal_uInt16 nContentTag,sal_uInt8 nContentVer,sal_uInt32 nContentSize)463 SfxSingleRecordWriter::SfxSingleRecordWriter
464 (
465 	SvStream*		pStream,		// Stream, in dem der Record angelegt wird
466 	sal_uInt16			nContentTag,	// Inhalts-Art-Kennung
467 	sal_uInt8			nContentVer,	// Inhalts-Versions-Kennung
468 	sal_uInt32			nContentSize	// Gr"o\se des Inhalts in Bytes
469 )
470 
471 /*  [Beschreibung]
472 
473 	Legt in 'pStream' einen 'SfxSingleRecord' an, dessen Content-Gr"o\se
474 	von vornherein bekannt ist.
475 */
476 
477 :	SfxMiniRecordWriter( pStream, SFX_REC_PRETAG_EXT,
478 						 nContentSize + SFX_REC_HEADERSIZE_SINGLE )
479 {
480 	// Erweiterten Header hinter den des SfxMiniRec schreiben
481 	*pStream << SFX_REC_HEADER( SFX_REC_TYPE_SINGLE, nContentTag, nContentVer);
482 }
483 
484 //=========================================================================
485 
ReadHeader_Impl(sal_uInt16 nTypes)486 inline FASTBOOL SfxSingleRecordReader::ReadHeader_Impl( sal_uInt16 nTypes )
487 
488 /*  [Beschreibung]
489 
490 	Interne Methode zum Einlesen eines SfxMultiRecord-Headers, nachdem
491 	die Basisklasse bereits initialisiert und deren Header gelesen ist.
492 	Ggf. ist ein Error-Code am Stream gesetzt, im Fehlerfall wird jedoch
493 	nicht zur"uckge-seekt.
494 */
495 
496 {
497 	FASTBOOL bRet;
498 
499 	// Basisklassen-Header einlesen
500 	sal_uInt32 nHeader=0;
501 	*_pStream >> nHeader;
502 	if ( !SetHeader_Impl( nHeader ) )
503 		bRet = sal_False;
504 	else
505 	{
506 		// eigenen Header einlesen
507 		*_pStream >> nHeader;
508 		_nRecordVer = sal::static_int_cast< sal_uInt8 >(SFX_REC_VER(nHeader));
509 		_nRecordTag = sal::static_int_cast< sal_uInt16 >(SFX_REC_TAG(nHeader));
510 
511 		// falscher Record-Typ?
512 		_nRecordType = sal::static_int_cast< sal_uInt8 >(SFX_REC_TYP(nHeader));
513 		bRet = 0 != ( nTypes & _nRecordType);
514 	}
515 	return bRet;
516 }
517 
518 //-------------------------------------------------------------------------
519 
SfxSingleRecordReader(SvStream * pStream)520 SfxSingleRecordReader::SfxSingleRecordReader( SvStream *pStream )
521 :	SfxMiniRecordReader()
522 {
523 	// Startposition merken, um im Fehlerfall zur"uck-seeken zu k"onnen
524     #ifdef DBG_UTIL
525 	sal_uInt32 nStartPos = pStream->Tell();
526 	DBG( DbgOutf( "SfxFileRec: reading record at %ul", nStartPos ) );
527     #endif
528 
529 	// Basisklasse initialisieren (nicht via Ctor, da der nur MiniRecs akzept.)
530 	Construct_Impl( pStream );
531 
532 	// nur Header mit korrektem Record-Type akzeptieren
533 	if ( !ReadHeader_Impl( SFX_REC_TYPE_SINGLE ) )
534 	{
535 		// Error-Code setzen und zur"uck-seeken
536 		pStream->SeekRel( - SFX_REC_HEADERSIZE_SINGLE );
537 		pStream->SetError( ERRCODE_IO_WRONGFORMAT );
538 	}
539 }
540 
541 //-------------------------------------------------------------------------
542 
SfxSingleRecordReader(SvStream * pStream,sal_uInt16 nTag)543 SfxSingleRecordReader::SfxSingleRecordReader( SvStream *pStream, sal_uInt16 nTag )
544 {
545 	// StartPos merken, um im Fehlerfall zur"uck-seeken zu k"onnen
546 	sal_uInt32 nStartPos = pStream->Tell();
547 
548 	// richtigen Record suchen, ggf. Error-Code setzen und zur"uck-seeken
549 	Construct_Impl( pStream );
550 	if ( !FindHeader_Impl( SFX_REC_TYPE_SINGLE, nTag ) )
551 	{
552 		// Error-Code setzen und zur"uck-seeken
553 		pStream->Seek( nStartPos );
554 		pStream->SetError( ERRCODE_IO_WRONGFORMAT );
555 	}
556 }
557 
558 //-------------------------------------------------------------------------
559 
FindHeader_Impl(sal_uInt16 nTypes,sal_uInt16 nTag)560 FASTBOOL SfxSingleRecordReader::FindHeader_Impl
561 (
562 	sal_uInt16		nTypes, 	// arithm. Veroderung erlaubter Record-Typen
563 	sal_uInt16		nTag		// zu findende Record-Art-Kennung
564 )
565 
566 /*	[Beschreibung]
567 
568 	Interne Methode zum lesen des Headers des ersten Record, der einem
569 	der Typen in 'nTypes' entspricht und mit der Art-Kennung 'nTag'
570 	gekennzeichnet ist.
571 
572 	Kann ein solcher Record nicht gefunden werden, wird am Stream ein
573 	Errorcode gesetzt, zur"uck-geseekt und sal_False zur"uckgeliefert.
574 */
575 
576 {
577 	// StartPos merken, um im Fehlerfall zur"uck-seeken zu k"onnen
578 	sal_uInt32 nStartPos = _pStream->Tell();
579 
580 	// richtigen Record suchen
581 	while ( !_pStream->IsEof() )
582 	{
583 		// Header lesen
584 		sal_uInt32 nHeader;
585 		DBG( DbgOutf( "SfxFileRec: searching record at %ul", _pStream->Tell() ) );
586 		*_pStream >> nHeader;
587 		if ( !SetHeader_Impl( nHeader ) )
588 			// EOR => Such-Schleife abbreichen
589 			break;
590 
591 		// Extended Record gefunden?
592 		if ( _nPreTag == SFX_REC_PRETAG_EXT )
593 		{
594 			// Extended Header lesen
595 			*_pStream >> nHeader;
596 			_nRecordTag = sal::static_int_cast< sal_uInt16 >(SFX_REC_TAG(nHeader));
597 
598 			// richtigen Record gefunden?
599 			if ( _nRecordTag == nTag )
600 			{
601 				// gefundener Record-Typ passend?
602 				_nRecordType = sal::static_int_cast< sal_uInt8 >(
603                     SFX_REC_TYP(nHeader));
604 				if ( nTypes & _nRecordType )
605 					// ==> gefunden
606 					return sal_True;
607 
608 				// error => Such-Schleife abbrechen
609 				break;
610 			}
611 		}
612 
613 		// sonst skippen
614 		if ( !_pStream->IsEof() )
615 			_pStream->Seek( _nEofRec );
616 	}
617 
618 	// Fehler setzen und zur"uck-seeken
619 	_pStream->SetError( ERRCODE_IO_WRONGFORMAT );
620 	_pStream->Seek( nStartPos );
621 	return sal_False;
622 }
623 
624 //=========================================================================
625 
SfxMultiFixRecordWriter(sal_uInt8 nRecordType,SvStream * pStream,sal_uInt16 nContentTag,sal_uInt8 nContentVer,sal_uInt32)626 SfxMultiFixRecordWriter::SfxMultiFixRecordWriter
627 (
628 	sal_uInt8			nRecordType,	// Subklassen Record-Kennung
629 	SvStream*		pStream,		// Stream, in dem der Record angelegt wird
630 	sal_uInt16			nContentTag,	// Content-Art-Kennung
631 	sal_uInt8			nContentVer,	// Content-Versions-Kennung
632 	sal_uInt32			              	// Gr"o\se jedes einzelnen Contents in Bytes
633 )
634 
635 /*  [Beschreibung]
636 
637 	Interne Methode f"ur Subklassen.
638 */
639 
640 :   SfxSingleRecordWriter( nRecordType, pStream, nContentTag, nContentVer ),
641 	_nContentCount( 0 )
642 {
643 	// Platz f"ur eigenen Header
644 	pStream->SeekRel( + SFX_REC_HEADERSIZE_MULTI );
645 }
646 
647 //------------------------------------------------------------------------
648 
SfxMultiFixRecordWriter(SvStream * pStream,sal_uInt16 nContentTag,sal_uInt8 nContentVer,sal_uInt32)649 SfxMultiFixRecordWriter::SfxMultiFixRecordWriter
650 (
651 	SvStream*		pStream,		// Stream, in dem der Record angelegt wird
652 	sal_uInt16			nContentTag,	// Content-Art-Kennung
653 	sal_uInt8			nContentVer,	// Content-Versions-Kennung
654 	sal_uInt32			             	// Gr"o\se jedes einzelnen Contents in Bytes
655 )
656 
657 /*  [Beschreibung]
658 
659 	Legt in 'pStream' einen 'SfxMultiFixRecord' an, dessen Content-Gr"o\se
660 	konstant und von vornherein bekannt ist.
661 */
662 
663 :   SfxSingleRecordWriter( SFX_REC_TYPE_FIXSIZE,
664 						   pStream, nContentTag, nContentVer ),
665 	_nContentCount( 0 )
666 {
667 	// Platz f"ur eigenen Header
668 	pStream->SeekRel( + SFX_REC_HEADERSIZE_MULTI );
669 }
670 
671 //------------------------------------------------------------------------
672 
Close(FASTBOOL bSeekToEndOfRec)673 sal_uInt32 SfxMultiFixRecordWriter::Close( FASTBOOL bSeekToEndOfRec )
674 
675 //	siehe <SfxMiniRecordWriter>
676 
677 {
678 	// Header noch nicht geschrieben?
679 	if ( !_bHeaderOk )
680 	{
681 		// Position hinter Record merken, um sie restaurieren zu k"onnen
682 		sal_uInt32 nEndPos = SfxSingleRecordWriter::Close( sal_False );
683 
684 		// gegen"uber SfxSingleRecord erweiterten Header schreiben
685 		*_pStream << _nContentCount;
686 		*_pStream << _nContentSize;
687 
688 		// je nachdem ans Ende des Records seeken oder hinter Header bleiben
689 		if ( bSeekToEndOfRec )
690 			_pStream->Seek(nEndPos);
691 		return nEndPos;
692 	}
693 
694 	// Record war bereits geschlossen
695 	return 0;
696 }
697 
698 //=========================================================================
699 
SfxMultiVarRecordWriter(sal_uInt8 nRecordType,SvStream * pStream,sal_uInt16 nRecordTag,sal_uInt8 nRecordVer)700 SfxMultiVarRecordWriter::SfxMultiVarRecordWriter
701 (
702 	sal_uInt8			nRecordType,	// Record-Kennung der Subklasse
703 	SvStream*       pStream,        // Stream, in dem der Record angelegt wird
704 	sal_uInt16			nRecordTag, 	// Gesamt-Art-Kennung
705 	sal_uInt8			nRecordVer		// Gesamt-Versions-Kennung
706 )
707 
708 /*  [Beschreibung]
709 
710 	Interner Ctor f"ur Subklassen.
711 */
712 
713 :   SfxMultiFixRecordWriter( nRecordType, pStream, nRecordTag, nRecordVer, 0 ),
714 	_nContentVer( 0 )
715 {
716 }
717 
718 //-------------------------------------------------------------------------
719 
SfxMultiVarRecordWriter(SvStream * pStream,sal_uInt16 nRecordTag,sal_uInt8 nRecordVer)720 SfxMultiVarRecordWriter::SfxMultiVarRecordWriter
721 (
722 	SvStream*       pStream,        // Stream, in dem der Record angelegt wird
723 	sal_uInt16			nRecordTag, 	// Gesamt-Art-Kennung
724 	sal_uInt8			nRecordVer		// Gesamt-Versions-Kennung
725 )
726 
727 /*  [Beschreibung]
728 
729 	Legt in 'pStream' einen 'SfxMultiVarRecord' an, dessen Content-Gr"o\sen
730 	weder bekannt sind noch identisch sein m"ussen, sondern jeweils nach dem
731 	Streamen jedes einzelnen Contents errechnet werden sollen.
732 
733 
734 	[Anmerkung]
735 
736 	Diese Methode ist nicht inline, da f"ur die Initialisierung eines
737 	<SvULongs>-Members zu viel Code generiert werden w"urde.
738 */
739 
740 :   SfxMultiFixRecordWriter( SFX_REC_TYPE_VARSIZE,
741 							 pStream, nRecordTag, nRecordVer, 0 ),
742 	_nContentVer( 0 )
743 {
744 }
745 
746 //-------------------------------------------------------------------------
747 
~SfxMultiVarRecordWriter()748 SfxMultiVarRecordWriter::~SfxMultiVarRecordWriter()
749 
750 /*	[Beschreibung]
751 
752 	Der Dtor der Klasse <SfxMultiVarRecordWriter> schlie\st den Record
753 	automatisch, falls <SfxMultiVarRecordWriter::Close()> nicht bereits
754 	explizit gerufen wurde.
755 */
756 
757 {
758 	// wurde der Header noch nicht geschrieben oder mu\s er gepr"uft werden
759 	if ( !_bHeaderOk )
760 		Close();
761 }
762 
763 //-------------------------------------------------------------------------
764 
FlushContent_Impl()765 void SfxMultiVarRecordWriter::FlushContent_Impl()
766 
767 /*	[Beschreibung]
768 
769 	Interne Methode zum Abschlie\sen eines einzelnen Contents.
770 */
771 
772 {
773 	// Versions-Kennung und Positions-Offset des aktuellen Contents merken;
774 	// das Positions-Offset ist relativ zur Startposition des ersten Contents
775 	_aContentOfs.Insert(
776 			SFX_REC_CONTENT_HEADER(_nContentVer,_nStartPos,_nContentStartPos),
777 			_nContentCount-1 );
778 }
779 
780 //-------------------------------------------------------------------------
781 
NewContent()782 void SfxMultiVarRecordWriter::NewContent()
783 
784 // siehe <SfxMultiFixRecordWriter>
785 
786 {
787 	// schon ein Content geschrieben?
788 	if ( _nContentCount )
789 		FlushContent_Impl();
790 
791 	// neuen Content beginnen
792 	_nContentStartPos = _pStream->Tell();
793 	++_nContentCount;
794 }
795 
796 //-------------------------------------------------------------------------
797 
Close(FASTBOOL bSeekToEndOfRec)798 sal_uInt32 SfxMultiVarRecordWriter::Close( FASTBOOL bSeekToEndOfRec )
799 
800 // siehe <SfxMiniRecordWriter>
801 
802 {
803 	// Header noch nicht geschrieben?
804 	if ( !_bHeaderOk )
805 	{
806 		// ggf. letzten Content abschlie\sen
807 		if ( _nContentCount )
808 			FlushContent_Impl();
809 
810 		// Content-Offset-Tabelle schreiben
811 		sal_uInt32 nContentOfsPos = _pStream->Tell();
812 		//! darf man das so einr"ucken?
813 		#if defined(OSL_LITENDIAN)
814 			_pStream->Write( _aContentOfs.GetData(),
815 							 sizeof(sal_uInt32)*_nContentCount );
816 		#else
817 			for ( sal_uInt16 n = 0; n < _nContentCount; ++n )
818 				*_pStream << sal_uInt32(_aContentOfs[n]);
819 		#endif
820 
821 		// SfxMultiFixRecordWriter::Close() "uberspringen!
822 		sal_uInt32 nEndPos = SfxSingleRecordWriter::Close( sal_False );
823 
824 		// eigenen Header schreiben
825 		*_pStream << _nContentCount;
826 		if ( SFX_REC_TYPE_VARSIZE_RELOC == _nPreTag ||
827 			 SFX_REC_TYPE_MIXTAGS_RELOC == _nPreTag )
828 			*_pStream << static_cast<sal_uInt32>(nContentOfsPos - ( _pStream->Tell() + sizeof(sal_uInt32) ));
829 		else
830 			*_pStream << nContentOfsPos;
831 
832 		// ans Ende des Records seeken bzw. am Ende des Headers bleiben
833 		if ( bSeekToEndOfRec )
834 			 _pStream->Seek(nEndPos);
835 		return nEndPos;
836 	}
837 
838 	// Record war bereits vorher geschlossen
839 	return 0;
840 }
841 
842 //=========================================================================
843 
NewContent(sal_uInt16 nContentTag,sal_uInt8 nContentVer)844 void SfxMultiMixRecordWriter::NewContent
845 (
846 	sal_uInt16		nContentTag,	// Kennung f"ur die Art des Contents
847 	sal_uInt8		nContentVer 	// Kennung f"ur die Version des Contents
848 )
849 
850 /*	[Beschreibung]
851 
852 	Mit dieser Methode wird in den Record ein neuer Content eingef"ugt
853 	und dessen Content-Tag sowie dessen Content-Version angegeben. Jeder,
854 	auch der 1. Record mu\s durch Aufruf dieser Methode eingeleitet werden.
855 */
856 
857 {
858 	// ggf. vorherigen Record abschlie\sen
859 	if ( _nContentCount )
860 		FlushContent_Impl();
861 
862 	// Tag vor den Content schreiben, Version und Startposition merken
863 	_nContentStartPos = _pStream->Tell();
864 	++_nContentCount;
865 	*_pStream << nContentTag;
866 	_nContentVer = nContentVer;
867 }
868 
869 //=========================================================================
870 
ReadHeader_Impl()871 FASTBOOL SfxMultiRecordReader::ReadHeader_Impl()
872 
873 /*  [Beschreibung]
874 
875 	Interne Methode zum Einlesen eines SfxMultiRecord-Headers, nachdem
876 	die Basisklasse bereits initialisiert und deren Header gelesen ist.
877 	Ggf. ist ein Error-Code am Stream gesetzt, im Fehlerfall wird jedoch
878 	nicht zur"uckge-seekt.
879 */
880 
881 {
882 	// eigenen Header lesen
883 	*_pStream >> _nContentCount;
884 	*_pStream >> _nContentSize; // Fix: jedes einzelnen, Var|Mix: Tabellen-Pos.
885 
886 	// mu\s noch eine Tabelle mit Content-Offsets geladen werden?
887 	if ( _nRecordType != SFX_REC_TYPE_FIXSIZE )
888 	{
889 		// Tabelle aus dem Stream einlesen
890 		sal_uInt32 nContentPos = _pStream->Tell();
891 		if ( _nRecordType == SFX_REC_TYPE_VARSIZE_RELOC ||
892 			 _nRecordType == SFX_REC_TYPE_MIXTAGS_RELOC )
893 			_pStream->SeekRel( + _nContentSize );
894 		else
895 			_pStream->Seek( _nContentSize );
896 		_pContentOfs = new sal_uInt32[_nContentCount];
897         memset(_pContentOfs, 0, _nContentCount*sizeof(sal_uInt32));
898 	//! darf man jetzt so einr"ucken
899 		#if defined(OSL_LITENDIAN)
900 			_pStream->Read( _pContentOfs, sizeof(sal_uInt32)*_nContentCount );
901 		#else
902 			for ( sal_uInt16 n = 0; n < _nContentCount; ++n )
903 				*_pStream >> _pContentOfs[n];
904 		#endif
905 		_pStream->Seek( nContentPos );
906 	}
907 
908 	// Header konnte gelesen werden, wenn am Stream kein Error gesetzt ist
909 	return !_pStream->GetError();
910 }
911 
912 //-------------------------------------------------------------------------
913 
SfxMultiRecordReader(SvStream * pStream)914 SfxMultiRecordReader::SfxMultiRecordReader( SvStream *pStream )
915     : _pContentOfs(0)
916     , _nContentSize(0)
917     , _nContentCount(0)
918     , _nContentNo(0)
919 {
920 	// Position im Stream merken, um im Fehlerfall zur"uck-seeken zu k"onnen
921 	_nStartPos = pStream->Tell();
922 
923 	// Basisklasse konstruieren (normaler Ctor w"urde nur SingleRecs lesen)
924 	SfxSingleRecordReader::Construct_Impl( pStream );
925 
926 	// Header der Basisklasse lesen
927 	if ( !SfxSingleRecordReader::ReadHeader_Impl( SFX_REC_TYPE_FIXSIZE |
928 				SFX_REC_TYPE_VARSIZE | SFX_REC_TYPE_VARSIZE_RELOC |
929 				SFX_REC_TYPE_MIXTAGS | SFX_REC_TYPE_MIXTAGS_RELOC ) ||
930 		 !ReadHeader_Impl() )
931 		// als ung"ultig markieren und zur"uck-seeken
932 		SetInvalid_Impl( _nStartPos );
933 }
934 
935 //-------------------------------------------------------------------------
936 
SfxMultiRecordReader(SvStream * pStream,sal_uInt16 nTag)937 SfxMultiRecordReader::SfxMultiRecordReader( SvStream *pStream, sal_uInt16 nTag )
938 :	_nContentNo(0)
939 {
940 	// Position im Stream merken, um im Fehlerfall zur"uck-seeken zu k"onnen
941 	_nStartPos = pStream->Tell();
942 
943 	// passenden Record suchen und Basisklasse initialisieren
944 	SfxSingleRecordReader::Construct_Impl( pStream );
945 	if ( SfxSingleRecordReader::FindHeader_Impl( SFX_REC_TYPE_FIXSIZE |
946 			SFX_REC_TYPE_VARSIZE | SFX_REC_TYPE_VARSIZE_RELOC |
947 			SFX_REC_TYPE_MIXTAGS | SFX_REC_TYPE_MIXTAGS_RELOC,
948 			nTag ) )
949 	{
950 		// eigenen Header dazu-lesen
951 		if ( !ReadHeader_Impl() )
952 			// nicht lesbar => als ung"ultig markieren und zur"uck-seeken
953 			SetInvalid_Impl( _nStartPos);
954 	}
955 }
956 
957 //-------------------------------------------------------------------------
958 
~SfxMultiRecordReader()959 SfxMultiRecordReader::~SfxMultiRecordReader()
960 {
961 	delete[] _pContentOfs;
962 }
963 
964 //-------------------------------------------------------------------------
965 
GetContent()966 FASTBOOL SfxMultiRecordReader::GetContent()
967 
968 /*	[Beschreibung]
969 
970 	Positioniert den Stream an den Anfang des n"chsten bzw. beim 1. Aufruf
971 	auf den Anfang des ersten Contents im Record und liest ggf. dessen
972 	Header ein.
973 
974 	Liegt laut Record-Header kein Content mehr vor, wird sal_False zur"uck-
975 	gegeben. Trotz einem sal_True-Returnwert kann am Stream ein Fehlercode
976 	gesetzt sein, z.B. falls er unvorhergesehenerweise (kaputtes File)
977 	zuende ist.
978 */
979 
980 {
981 	// noch ein Content vorhanden?
982 	if ( _nContentNo < _nContentCount )
983 	{
984 		// den Stream an den Anfang des Contents positionieren
985 		sal_uInt32 nOffset = _nRecordType == SFX_REC_TYPE_FIXSIZE
986 					? _nContentNo * _nContentSize
987 					: SFX_REC_CONTENT_OFS(_pContentOfs[_nContentNo]);
988 		sal_uInt32 nNewPos = _nStartPos + nOffset;
989 		DBG_ASSERT( nNewPos >= _pStream->Tell(), "SfxMultiRecordReader::GetContent() - New position before current, to much data red!" );
990 
991 		// #99366#: correct stream pos in every case;
992         // the if clause was added by MT  a long time ago,
993         // maybe to 'repair' other corrupt documents; but this
994         // gives errors when writing with 5.1 and reading with current
995         // versions, so we decided to remove the if clause (KA-05/17/2002)
996         // if ( nNewPos > _pStream->Tell() )
997 		_pStream->Seek( nNewPos );
998 
999 		// ggf. Content-Header lesen
1000 		if ( _nRecordType == SFX_REC_TYPE_MIXTAGS ||
1001 			 _nRecordType == SFX_REC_TYPE_MIXTAGS_RELOC )
1002 		{
1003 			_nContentVer = sal::static_int_cast< sal_uInt8 >(
1004                 SFX_REC_CONTENT_VER(_pContentOfs[_nContentNo]));
1005 			*_pStream >> _nContentTag;
1006 		}
1007 
1008 		// ContentNo weiterz"ahlen
1009 		++_nContentNo;
1010 		return sal_True;
1011 	}
1012 
1013 	return sal_False;
1014 }
1015 
1016 
1017