*****************************************************************************************************
* WP File I/O Routines
* 
*    This file contains the routines used to read and write WP scraps & PL text threads.
* 
*****************************************************************************************************

	load	'macros.dump'
	include 	'driver.equ'
	include 	'scrap.equ'

	IMPORT	D_AlertBox
	IMPORT	D_BeachBall
	IMPORT	D_CurCursor
	IMPORT	D_Deref
	IMPORT	D_NeedHandle
	IMPORT	D_Read2
	IMPORT	D_ReadHandle2
	IMPORT	D_SetCursor
	IMPORT	D_WriteHandle2
	IMPORT	D_MustHandle
	IMPORT	D_Write2
	IMPORT	D_SetMark2
	
	IMPORT	X_DisposeWPScrap
	IMPORT	X_MassageWPScrap
	IMPORT	X_OldCursor
	IMPORT	X_ParBlockSize
	IMPORT	X_ReadWPScrap
	IMPORT	X_RulerSize
	IMPORT	X_WriteThread

*****************************************************************************************************
; X_ScReadWPScrap ( FileRef:word, Type:word )
;
;   This routine is called to read both WPScraps and PL text threads from disk. If Type is non-zero
; then read text thread else read WPscrap. FileRef should be the file reference to the already opened
; and correctly positioned file. Memory and disk errors are returned.
;
;   It is assumed that the bank has already been set to the Scrap segments bank.

X_ScReadWPScrap	PROC	EXPORT

	;Using X_WPScrapData
	;Using X_WPFileData
	;Using D_IOData
	;Using X_ClipData
	;Using D_CursorData

	input 	FileRef:w,Type:w
	local 	Bpars:w,SaveArraySize:l,SaveArrayHand:l,SAPtr:l
	local 	Blocks:w,Rulers:w,Picts:w
	local 	BListhand:l,BLPtr:l,RListHand:l,RLPtr:l,PListHand:l,PLPtr:l
	local 	Block:l,Ruler:l,ScrapSize:l,ScrapPtr:l,RulerPtr:l
	local 	PBlockSize:w,Scratch:l
	local	Handles:w,RHands:w,BHands:w
	output 	Scrap:l
	error 	err
	begin

; Save the current cursor and put up the beachball cursor.

	moveword 	>D_CurCursor,X_OldCursor
	jsl	D_Beachball
	
; Set PBlockSize depending on whether we are reading in a PL text thread or a WP scrap.
	
	lda 	Type
	bne 	notwp
	moveword 	#X_ParBlockSize,PBlockSize
	bra 	cont
notwp	moveword 	#(X_ParBlockSize+4),PBlockSize

; Read in the # of paragraph blocks in the file. Use this count to create temporary array into which
; we will read the paragraph info. Create the handle for the scrap we will be filling in with the
; information we read from disk. If any memory or disk errors occur dispose of the memory we have
; allocated and exit.

cont	call	D_Read2,in=(FileRef:w,!Bpars:l,#2:l),out=(ax:l),err=err
	jcs 	exit

	tool	_Multiply,in=(BPars:w,#12:w),out=(SaveArraySize:l)
	call	D_NeedHandle,in=(SaveArraySize:l,#$8000:w),out=(SaveArrayHand:l),err=err
	jcs 	exit
	movelong	[SaveArrayHand],SAPtr
	call	D_Read2,in=(FileRef:w,SAPtr:l,SaveArraySize:l),out=(ax:l),err=err
	jcs	killsave
	
	tool	_Multiply,in=(Bpars:w,PBlockSize:w),out=(ScrapSize:l)
	addlong	ScrapSize,#2,ScrapSize
	call	D_NeedHandle,in=(ScrapSize:l,#$8000:w),out=(Scrap:l),err=err
	jcs	killsave

; Put the number of paragraphs into the first word of the scrap, then bump scrapptr past the
; paragraph count. Initialize the number of text blocks & rulers to zero.

	movelong 	[Scrap],ScrapPtr
	moveword 	BPars,[ScrapPtr]
	addlong 	ScrapPtr,#2,ScrapPtr
	
	stz 	Blocks
	stz 	Rulers
	stz 	Picts
	
; For all paragraph blocks do: copy paragraph info to the scrap; keep track of number of rulers and
; text blocks encountered.

parloop	lda 	Bpars
	jeq 	doneloop

textblock	cmpw	[SAPtr],Blocks
	blt	nonewblock
	inc 	Blocks

nonewblock	lda 	Type
	bne 	realruler
	ldy	#4
	lda	[SAPtr],y
	bne 	incloop

realruler	cmpw	[SAPtr]:#6,Rulers
	blt	incloop
	inc 	Rulers

incloop	moveword 	[SAPtr]:#2,[ScrapPtr]:#X_scpOffset
	moveword 	[SAPtr]:#4,[ScrapPtr]:#X_scpAttr
	moveword 	[SAPtr]:#8,[ScrapPtr]:#X_scpPixels
	moveword 	[SAPtr]:#10,[ScrapPtr]:#X_scpLastLine
	movelong 	#0,[ScrapPtr]:#X_scpLinehand
	addlong 	SAPtr,#12,SAPtr
	addwl 	PBlockSize,ScrapPtr
	dec 	Bpars
	brl 	parloop

; Create handle to store ruler handles in until we are sure we will succeed.

doneloop	lda	Rulers
	asl	a
	asl	a
	ldx	#0
	call	D_NeedHandle,in=(ax:l,#$8000:w),out=(RListHand:l),err=err
	jcs	killscrap
	movelong 	[RListHand],RLPtr

; Read in all the rulers remembering their handles in the ruler list so we can recover from errors.

	stz 	RHands
ruleinloop	jsl	D_BeachBall
	cmpw	RHands,Rulers
	jge	endrloop

	call	D_NeedHandle,in=(#X_RulerSize:l,#$8000:w),out=(Ruler:l),err=err
	jcs 	killrulers
	movelong	[Ruler],RulerPtr
	
	lda	RHands
	asl	a
	asl	a
	tay
	movelong	Ruler,[RLPtr]:y
	inc	RHands

	call	D_Read2,in=(FileRef:w,RulerPtr:l,#X_RulerSize:l),out=(ax:l),err=err
	jcs 	killrulers
	tool	_HUnlock,in=(Ruler:l)
	brl	ruleinloop	

; If no text blocks exist then branch to handle pictures (ha ha).

endrloop	lda	Blocks
	jeq	dopicts

; Create handle to store text block handles in until we are sure we will succeed.

	lda	Blocks
	asl	a
	asl	a
	ldx	#0
	call	D_NeedHandle,in=(ax:l,#$8000:w),out=(BListHand:l),err=err	
	jcs 	killrulers
	movelong 	[BListHand],BLPtr

; Read in all the text blocks putting their handles in the block list so we can recover from errors.

	stz	BHands
readbloop	jsl	D_BeachBall
	call	D_ReadHandle2,in=(FileRef:w),out=(Block:l),err=err
	jcs 	killblocks
	tool	_HUnlock,in=(Block:l)

	lda	BHands
	asl	a
	asl	a
	tay
	movelong	Block,[BLPtr]:y
	inc	BHands
	cmpw	BHands,Blocks
	blt	readbloop

; Handle reading picts ( picts not supported at this time ).

dopicts

; Use the reference indexes in the save array to put the text block & ruler handles we have read in
; the scrap paragraphs.

doinsert	movelong 	[SaveArrayHand],SAPtr
	movelong 	[Scrap],ScrapPtr
	moveword 	[ScrapPtr],Bpars
	addlong 	ScrapPtr,#2,ScrapPtr

	stz 	Handles
insertloop	moveword 	[SAPtr],a
	asl 	a
	asl 	a
	tay
	movelong 	[BLPtr]:y,[ScrapPtr]
	lda 	Type
	bne 	dorulesub
	moveword 	[SAPtr]:#4,a
	bne 	iloopinc

dorulesub	moveword 	[SAPtr]:#6,a
	asl a
	asl a
	tay
	movelong 	[RLPtr]:y,[ScrapPtr]:#X_scpRulerHand

iloopinc	inc 	Handles
	addwl 	#12,SAptr
	addwl 	PBlocksize,ScrapPtr
	cmpw	Handles,Bpars
	jlt 	insertloop

; Successfully read in the scrap. Unlock the scrap and dispose of the text block & ruler lists.
; Dispose of the save array and exit with no error.

readdone	tool	_HUnlock,in=(Scrap:l)
	lda	Blocks
	beq	chkrlist
	tool	_DisposeHandle,in=(BListHand:l)
chkrlist	lda	Rulers
	beq	gokillsave
	tool	_DisposeHandle,in=(RListHand:l)
gokillsave	brl	killsave

; Error occurred. Dispose of all memory allocated so far.

killblocks	lda	BHands
	beq	killblist
	dec	a
	asl	a
	asl	a
	tay
	tool	_DisposeHandle,in=([BLPtr]:y:l)
	dec	BHands
	bra	killblocks
killblist	tool	_DisposeHandle,in=(BListHand:l)

killrulers	lda	RHands
	beq	killrlist
	dec	a
	asl	a
	asl	a
	tay
	tool	_DisposeHandle,in=([RLPtr]:y:l)
	dec	RHands
	bra	killrulers
killrlist	tool	_DisposeHandle,in=(RListHand:l)

killscrap	tool	_DisposeHandle,in=(Scrap:l)
killsave	tool	_DisposeHandle,in=(SaveArrayHand:l)
exit	call	D_SetCursor,in=(X_OldCursor:w)
	return

	ENDP


*****************************************************************************************************
; X_ScWriteWPScrap ( FileRef:word, Type:word )
;
;   This routine is called to write both WPScraps and PL text threads from disk. If Type is non-zero
; then write text thread else write WPscrap. FileRef should be the file reference to the already 
; opened and correctly positioned file. Memory and disk errors are returned.
;
;   It is assumed that the bank has already been set to the Scrap segments bank.

X_ScWriteWPScrap	PROC	EXPORT

	input 	FileRef:w,Scrap:l,Type:w
	local 	Sptr:l,Pars:w
	error 	err
	begin

; Lock down the scrap; point past the paragraph count and call write thread to do the actual work.

	tool	_HLock,in=(Scrap:l)
	movelong	[Scrap],Sptr
	moveword 	[Sptr],Pars
	addwl 	#2,Sptr
	call	X_WriteThread,in=(FileRef:w,Pars:w,Sptr:l,Type:w),err=err

	return
	ENDP

*****************************************************************************************************
; X_ScWriteThread ( FileRef:word, Pars:word, Scrap:long, Type:word )
;

X_ScWriteThread	PROC	EXPORT
	;Using X_WPScrapData
	;Using X_WPFileData
	;Using D_IOData
	;Using X_ClipData
	;Using D_CursorData

	input 	FileRef:w,Pars:w,Scrap:l,Type:w
	local 	Ptr:l,Bpars:w,SaveArraySize:l,SaveArrayHand:l,SAPtr:l
	local 	BListPtr:l,BListhand:l,BLPtr:l,RListHand:l,RLPtr:l
	local 	PicListHand:l,PLPtr:l,Blocks:w,Rulers:w,Picts:w
	local 	BListSize:l,Block:l,Ruler:l,ScrapPtr:l
	local 	PBlockSize:w,Scratch:l,ScratchSize:l
	local 	ScratchPtr:l,Temp2:l,Temp:l
	error 	err
	begin
	
; Save the current cursor and put up the beachball cursor.

	moveword 	>D_CurCursor,X_OldCursor
	jsl	D_Beachball
	stz 	err

; Set PBlockSize depending on whether we are writing a PL text thread or a WP scrap.
	
	lda 	Type
	bne 	notwp
	moveword 	#X_ParBlockSize,PBlockSize
	bra 	cont
notwp	moveword 	#(X_ParBlockSize+4),PBlockSize

; Setup local variables. Ptr will point to current location in document scrap and Bpars will contain
; the number of paragraphs in the scrap to go.

cont	movelong 	Scrap,Ptr
	moveword 	Pars,Bpars

; Yuk! We must copy all the paragraph information to a separate array where we will modify things
; like the ruler and text block handles to be indexes instead of handles. ( ie make it a semi-flat
; structure to save to disk. ) If we get a memory error then pass it back to the caller.

	tool	_Multiply,in=(Bpars:w,#12:w),out=(SaveArraySize:l)
	call	D_MustHandle,in=(SaveArraySize:l,#0:w),out=(SaveArrayHand:l),err=err
	jcs 	exit

; Yuk! again! We must create lookup array for text block handles.

	lda	Bpars
	asl	a
	asl	a
	sta	BListSize
	ldx	#0
	call	D_MustHandle,in=(ax:l,#0:w),out=(BListHand:l),err=err
	jcs	killsave

; Yuk! Yuk! We must create lookup array for ruler handles.

	call	D_MustHandle,in=(#0:w,BListSize:w,#0:w),out=(RListHand:l),err=err
	jcs	killblist

; Lock down the handle and get their pointers.

	rcall	D_Deref,in=(SaveArrayHand:ax),out=(SAPtr:ax)
	rcall	D_Deref,in=(BListHand:ax),out=(BLPtr:ax)
	rcall	D_Deref,in=(RListHand:ax),out=(RLPtr:ax)
	
; Clear counts and initialize variables before loop.

	stz 	Blocks	
	stz 	Rulers
	stz 	Picts
	stzl 	Block
	movelong 	Scrap,Ptr

; For all paragraphs do: 

parloop	lda 	Bpars	  
	jeq 	endloop

; Check if first time through if so add first text block in scrap to the block lookup array.

	lda	Block
	ora	Block+2
	bne	notfirst
	movelong	[Ptr],Block
	bra 	addtolist

; Check if the text block currently being pointed to in the scrap is the same as the last text block
; we saw. If not add it to the block lookup array and make it the last block seen. If it is the same
; block then branch to handle the current paragraph ruler.

notfirst	cmpl 	[Ptr],Block	   
	beq 	dorulers

addtolist	lda 	Blocks
	asl 	a
	asl 	a
	tay
	movelong 	[Ptr],[BLPtr]:y
	inc 	Blocks	 
	movelong 	[Ptr],Block	   

; Check if ruler exists. If WP scrap and Attr is non-zero then must be a page break paragraph and
; therefore won't have a ruler associated with it. In all other cases ruler should exist.

dorulers	lda 	Type
	bne 	realruler
	ldy	#X_scpAttr
	lda	[Ptr],y
	jne 	endruler

; Scan through ruler lookup array trying to find the same ruler handle as the current one {temp}.

realruler	ldx 	#0
	movelong	[Ptr]:#X_scpRulerHand,Temp
rulerloop	txa	   
	cmp 	Rulers
	bge 	nofindruler
	asl 	a
	asl 	a
	tay
	cmpl 	Temp,[RLPtr]:y
	beq 	rulerfound
	inx
	bra 	rulerloop

; Ruler found so put its lookup value in the save array.

rulerfound	txa
	bra	putrulenum

; Ruler was not found so we need to add it to the lookup array and bump the ruler count. 

nofindruler	txa
	asl 	a
	asl 	a
	pha
	ldy 	#X_scpRulerHand
	lda 	[Ptr],y
	ply
	phy
	sta 	[RLPtr],y
	ldy 	#X_scpRulerHand+2
	lda 	[Ptr],y
	ply
	iny
	iny
	sta 	[RLPtr],y
	inc 	Rulers
	txa

putrulenum	moveword 	a,[SAPtr]:#6

; Save the lookup value for the current text block, and the remaining paragraph information.

endruler	lda 	Blocks	 
	dec 	a
	sta 	[SAPtr]	
	moveword 	[Ptr]:#X_scpOffset,[SAPtr]:#2
	moveword 	[Ptr]:#X_scpAttr,[SAPtr]:#4
	moveword 	[Ptr]:#X_scpPixels,[SAPtr]:#8
	moveword 	[Ptr]:#X_scpLastLine,[SAPtr]:#10

; Point save array pointer past the paragraph info we just saved. Bump the scrap pointer past the
; paragraph we just processed. Decrement the number of paragraphs left to do and go back for more.

	addlong 	SAPtr,#12,SAPtr
	addwl 	PBlockSize,Ptr
	dec 	Bpars
	brl 	parloop

; Ok we have filled up the save array with all the paragraphs info. Ruler & text block handles have
; been replaced with their lookup values. No save out the paragraph cout and the save array.

endloop	moveword	Pars,Bpars
	call	D_Write2,in=(FileRef:w,!Bpars:l,#2:l),out=(ax:l),err=err
	jcs 	killrlist

	call	D_Write2,in=(FileRef:w,[SaveArrayHand]:l,SaveArraySize:l),out=(ax:l),err=err
	jcs 	killrlist

; Write out rulers

	movelong	RLPtr,temp
ruleoutloop	jsl	D_BeachBall
	lda 	Rulers
	beq 	endruleout
	movelong 	[temp],Ruler
	tool	_HLock,in=(Ruler:l)
	call	D_Write2,in=(FileRef:w,[Ruler]:l,#X_RulerSize:l),out=(ax:l),err=err
	jcs 	killrlist
	tool	_HUnlock,in=(Ruler:l)
	addwl	#4,temp
	dec 	Rulers
	bra 	ruleoutloop
endruleout

; Write out paragraph blocks. Paragraph blocks are written out as a long for the size of the
; paragraph and then the paragraph block is written out. The text block is shrunk by writing
; out the used size for both the block size & used size.

	movelong	BLPtr,temp
paroutloop	jsl	D_BeachBall
	lda 	Blocks
	jeq 	dopictout

	movelong	[temp],Scratch
	movelong 	[Scratch],ScratchPtr
	ldy 	#2
	lda 	[ScratchPtr],y
	sta 	ScratchSize
	stz 	ScratchSize+2
	call	D_Write2,in=(FileRef:w,!ScratchSize:l,#4:l),out=(ax:l),err=err
	jcs 	killrlist

	addwl	#2,ScratchPtr
	call	D_Write2,in=(FileRef:w,ScratchPtr:l,#2:l),out=(ax:l),err=err
	jcs	killrlist
	
	sublong	ScratchSize,#2,ScratchSize
	call	D_Write2,in=(FileRef:w,ScratchPtr:l,ScratchSize:l),out=(ax:l),err=err
	jcs	killrlist
	
	addwl	#4,temp
	dec	Blocks
	brl 	paroutloop

; Write out picture blocks ( not implemented in this version ).

dopictout

; Dispose of all the memory that we allocated to save the scrap/thread.

killrlist	tool	_DisposeHandle,in=(RListHand:l)
killblist	tool	_DisposeHandle,in=(BListHand:l)
killsave	tool	_DisposeHandle,in=(SaveArrayHand:l)
exit	call	D_SetCursor,in=(X_OldCursor:w)
	return

	ENDP

*****************************************************************************************************
; X_ScImportWP ( FileRef:word )
;
;   This routine will read from the given WP document and create a WP scrap from it. If the document
; is blank then warn the user and return an error. Memory & disk errors will be returned.

X_ScImportWP	PROC	EXPORT
	;Using D_IOData
	;Using X_WPFileData

	input 	FileRef:w
	local 	Offset:w,TotalOffset:l,Temp:l,Temp2:l,FileRef:w
	output 	Dest:l
	error 	err
	begin

; Set the file mark after the file header, and the wp header. This is a hard wired magic number!!
; Yeah!! Isn't programming fun.

	call	D_SetMark2,in=(FileRef:w,#0:w,#$29C:l),err=err

; Read the WP document as a scrap.

	call	X_ReadWPScrap,in=(FileRef:w,#0:w),out=(Dest:l),err=err
	bcs 	exit

; Separate the first and last paragraphs into their own text blocks.

	call	X_MassageWPScrap,in=(Dest:l),err=err
	bcc	checkblank
	call	X_DisposeWPScrap,in=(Dest:l)
	stzl	Dest
	bra	exit

; If there is one paragraph and its text block is of size 12 then warn user that document is blank.

checkblank	movelong 	[Dest],Temp
	Cmpw 	[Temp],#2
	bge 	exit
	movelong 	[Temp]:#2,Temp2
	movelong 	[Temp2],Temp
	Cmpw 	[Temp]:#2,#13
	bge 	exit
	call 	D_AlertBox,in=(#OKBox:w,#BlankScrap:l),out=(a:w)
	call	X_DisposeWPScrap,in=(Dest:l)
	movelong 	#-1,err

exit	return

BlankScrap	STR 'This document is blank.'

	ENDP
	END

