*****************************************************************************************************
; PL I/O Routines
; 
;    This file contains the routines used by PL to open and save documents.
;
*****************************************************************************************************


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

	IMPORT	D_BeachBall
	IMPORT	D_Deref
	IMPORT	D_DestroyByRef
	IMPORT	D_FastMult
	IMPORT	D_NeedHand
	IMPORT	D_Read2
	IMPORT	D_ReadHandle2
	IMPORT	D_SetCursor
	IMPORT	D_UnLock
	IMPORT	D_Write2
	IMPORT	D_WriteHandle2

	IMPORT	P_CheckHand
	IMPORT	P_CurrentWin
	IMPORT	P_ErrorAlert
	IMPORT	P_Header
	IMPORT	P_HeaderSize
	IMPORT	P_HorizGCount
	IMPORT	P_HorizGuides
	IMPORT	P_InitDoc
	IMPORT	P_KillPage
	IMPORT	P_LastObj
	IMPORT	P_LoadPage
	IMPORT	P_LoadWindowStuff
	IMPORT	P_MergeGuides
	IMPORT	P_NotActive
	IMPORT	P_OBJSIZES
	IMPORT	P_ObjList
	IMPORT	P_PInfoSize
	IMPORT	P_Page
	IMPORT	P_PageArray
	IMPORT	P_PageCount
	IMPORT	P_SavePage
	IMPORT	P_SaveWindowStuff
	IMPORT	P_ShowDoc
	IMPORT	P_UseTemplate
	IMPORT	P_Version
	IMPORT	P_VertGCount
	IMPORT	P_VertGuides

	IMPORT	T_ReThread

	IMPORT	X_ReadWPScrap
	IMPORT	X_WriteWPScrap
	IMPORT	X_DisposeThread

*****************************************************************************************************
; P_Thread2Pair ( ThreadHdl:long ): PageNo:word, ObjNo:word
;
;   This routine is called to determine the page and object number for a given text thread. The page
; and object numbers will be used when saving to disk to replace the handles. These values can be
; used on an Open to correctly relink the new handles in the same way.

P_Thread2Pair PROC	EXPORT
	;Using 	P_Data
	;Using 	P_ObjData

	input  	ThreadHdl:l
	output 	PageNo:w,ObjNo:w
	local  	Hdl:l,Ptr:l,ThreadPtr:l,ObjHdl:l,ObjPtr:l,Offset:l
	begin

; If thread handle is NIL then blow it off.

	cpzl ThreadHdl
	beq NoThread

; Get page number from the thread.

	movelong [ThreadHdl],ThreadPtr
	moveword [ThreadPtr]:#P_PageNo,PageNo

; Get pointer to page entry in the page array.

	tool _Multiply,in=(PageNo:w,#P_PInfoSize:w),out=(Offset:l)
	movelong P_PageArray,Hdl
	movelong [Hdl],Ptr
	addlong Ptr,Offset,Ptr

; Scan page object list to see if we can find the thread we are looking for. Get its object number.

	movelong [Ptr],ObjHdl
	stz ObjNo

loop	cmpl ObjHdl,ThreadHdl
	beq exit
	movelong [ObjHdl],ObjPtr
	movelong [ObjPtr],ObjHdl
	inc ObjNo
	bra loop

; The thread was NIL so return page number and object number of -1.

NoThread	moveword #-1,PageNo
	sta ObjNo

exit	return
	ENDP

*****************************************************************************************************
; P_Pair2Thread ( PageNo:word, ObjNo:word ): ThreadHdl:long
;
;   This routine is called to determine the text thread corresponding to the given text object for
; the given page.

P_Pair2Thread PROC	EXPORT
	;Using 	P_Data
	;Using 	P_ObjData

	input  	PageNo:w,ObjNo:w
	output 	ObjHdl:l
	local  	Hdl:l,Ptr:l,ObjPtr:l,Offset:l
	begin

; If page number is -1 then no thread exists for this page object; return NIL.

	cmpw PageNo,#-1
	beq NoThread

; Get pointer to page entry in the page array.

	tool _Multiply,in=(PageNo:w,#P_PInfoSize:w),out=(Offset:l)
	movelong P_PageArray,Hdl
	movelong [Hdl],Ptr
	addlong Ptr,Offset,Ptr

; Scan object array for the given page to find the object handle for the given object number.

	movelong [Ptr],ObjHdl
	ldx #0
loop	cpx ObjNo
	beq exit
	movelong [ObjHdl],ObjPtr
	movelong [ObjPtr],ObjHdl
	inx
	bra loop

; No thread exists for this page/object return NIL.

NoThread	stzl ObjHdl

exit	return
	ENDP

*****************************************************************************************************
; P_SetParag ( ObjectPtr:long )
;
;   This routine is called to put the given objects paragraph handle into all the text threads in
; its linked list.

P_SetParag	PROC	EXPORT
	;Using 	P_ObjData

	input 	objptr:l
	local 	objhdl:l,paraghdl:l
	begin

	movelong 	[objptr]:#P_ParagHdl,paraghdl
objloop	cpzl 	[objptr]:#P_NThread
	beq 	exit
	movelong 	[objptr]:#P_NThread,objhdl
	movelong 	[objhdl],objptr
	movelong 	paraghdl,[objptr]:#P_ParagHdl
	bra 	objloop

exit	return
	ENDP

*****************************************************************************************************
; P_OpenDoc ( FileId:word, WindowPtr:long )
;
;   This routine will handle reading in a page layout document with the given file reference. The
; file has already been opened by the driver and the file mark moved to the start of the document
; data. Memory or file I/O errors are returned to the driver which will handle closing the file and
; alerting the user.

P_OpenDoc	PROC	EXPORT
	;Using 	P_Data
	;Using 	P_ObjData
	;Using 	D_IOData
	;Using 	P_RulerData

	input 	fileid:w,window:l
	local 	page:w,pagecount:w,oldpage:w,tmp:l,tmphdl:l
	local 	oldwin:l,tmpobj:l,objptr:l,xhdl:l,xtype:w
	local 	xobj:l,xobjhdl:l,table:l,refcon:l,lastpage:w

	; these 4 variables are clustered. Do not rearrange or seperate.

	local 	templ:w,hguide:w,vguide:w,count:w
	error 	err
	begin 	+b

; Clear local variables and put up the watch cursor.

	stzl 	tmp
	stzl 	tmphdl
	stzl 	xobj
	stzl 	xobjhdl
	stzl 	table
	stzl 	refcon
	stzl 	oldwin
	stzl 	tmpobj
	stzl 	objptr
	stz 	pagecount
	call 	D_SetCursor,in=(#WatchCursor:w)

; Page out current window information. Set table to point to table of object sizes.

	movelong 	P_CurrentWin,oldwin
	call 	P_SaveWindowStuff,in=(oldwin:l)
	movelong 	#P_OBJSIZES,table

; If we can't get 32k then probably shouldn't be trying to open a document. Return driver a memory
; error.

	call 	P_CheckHand,in=(#$8000:l),err=(err)
	jcs 	exit

; Read header information for the file. If read error then return with error to driver.

	call 	D_Read2,in=(fileid:w,#P_Header:l,#P_HeaderSize:l),out=(ax:l),err=(err)
	jcs 	exit

; Check if PL id present in file if not then this file must be damaged or not a real PL file. Since
; we are handling the error return -1 to the driver.

	jsr	chkversion
	jcs	exit

; Traverse the document page by page, object by object...

; File looks OK so far; clear error status and get temp object for use in loading file. If we can't 
; get it return to the driver with a mem error, else lock down the temp object and clear the temp
; object handle variable.

	stz	err
	call 	D_NeedHand,in=(#P_TextObjSize:l),out=(xobjhdl:l),err=(err)
	jcs 	exit
	rcall 	D_Deref,in=(xobjhdl:ax),out=(xobj:ax)
	movelong	#0,xhdl

; Initialize the document structure. Get refcon in case we need to dispose it on error.

	call 	P_InitDoc,in=(window:l),out=(refcon:l),err=err
	jcs	killxobj

; Initialize pagecount (including master pages) and current/last page.

	addword 	P_PageCount,#2,pagecount
	moveword 	#-1,page
	sta 	lastpage

; Try to allocate a page array large enough to hold all the pages in the document. If we can't then
; dispose of the temp object and the refcon we allocated.

	spacelong
	tool 	_Multiply,in=(#P_PInfoSize:w,pagecount:w),out=(:l)
	call 	D_NeedHand,err=(err)
	pulllong 	P_PageArray
	jcs 	killrefcon

; Remember what the old page used to be. Clear loop variables and bump to next page and last page.

	moveword 	P_Page,oldpage
pageloop	stzl 	P_HorizGuides
	stzl 	P_VertGuides
	stzl 	P_ObjList
	stzl 	P_LastObj
	inc 	page
	inc 	lastpage

; Read count, vguide, hguide and templ in one call. If error reading then dispose of all pages read
; so far, refcon, and xobj before returning mem error to the driver. Set global based on values read.
	
	call 	D_Read2,in=(fileid:w,!count:l,#8:l),out=(ax:l),err=(err)
	jcs 	killpages			
	moveword 	vguide,P_VertGCount
	moveword 	hguide,P_HorizGCount
	moveword 	templ,P_UseTemplate

; Read in objects from file.

objloop	jsl	D_BeachBall
	dec 	count
	jmi 	doneobj

; Get object information. If error then dispose of all memory allocated so far.

	stzl	xhdl
	call 	D_Read2,in=(fileid:w,xobj:l,#P_TextObjSize:l),out=(ax:l),err=(err)
	jcs 	killpages

; Check if object type is text object. If so check if text object has a thread associated with it.
; If not then ready to add object to page layout, else read in text thread for the object.

	cmpw 	[xobj]:#P_Type,#P_ALPHA
	bne 	CkPict
	cmpw 	[xobj]:#P_PThread,#-1
	jne 	DidObj
	call	X_ReadWPScrap,in=(FileId:w,#1:w),out=(tmp:l),err=err
	jcs 	killpages	

; Put text thread into the temp object. Unlock the text thread since read WP returns it locked.

	movelong	tmp,xhdl
	moveword	#P_Alpha,xtype
	movelong 	xhdl,[xobj]:#P_ParagHdl
	rcall 	D_UnLock,in=(xhdl:ax)
	bra 	DidObj

; Check if object type is PICT. If so read in PICT handle.

CkPict	cmp 	#P_PICTURE
	bne 	DidObj

	call 	D_ReadHandle2,in=(fileid:w),out=(tmp:l),err=(err)
	jcs 	killpages	

; Put picture handle in temp object. Unlock the pict handle since read handle returns it locked.

	movelong	tmp,xhdl
	moveword	#P_Picture,xtype
	movelong 	xhdl,[xobj]:#P_PictHandle
	rcall 	D_UnLock,in=(xhdl:ax)

; Xobj now has entire object including picture or text thread handles. Check if valid object type
; if not the file must be corrupt; warn user and dispose of all memory so far allocated.

DidObj	cmpw 	[xobj]:#P_Type,#P_Picture+1
	blt 	oktype
	moveword 	#-1,err
	call 	P_ErrorAlert,in=(#2:w)
	brl 	killpages

; Make copy of temp object to put in the data structure. If memory error then dispose of all memory
; so far allocated and return with mem error. { use table to determine true size needed for object }

oktype	dec 	a
	asl 	a
	asl 	a
	tay
	movelong 	[table]:y,tmp
	call 	D_NeedHand,in=(tmp:l),out=(tmphdl:l),err=(err)
	jcs 	killpages
	tool 	_BlockMove,in=(xobj:l,[tmphdl]:l,tmp:l)
	stzl	xhdl

; Get pointer to new object. Add object to linked list.

	movelong 	[tmphdl],tmp
	IsNil 	P_ObjList
	bne 	NotFirst
	movelong 	tmphdl,P_ObjList
	movelong 	tmphdl,P_LastObj
	movelong 	#0,[tmp]
	movelong 	#0,[tmp]:#P_Prev
	bra 	DidInsert

NotFirst	movelong 	P_LastObj,[tmp]:#P_Prev
	movelong 	#0,[tmp]
	movelong 	tmphdl,P_LastObj
	movelong 	[tmp]:#P_Prev,tmphdl
	movelong 	[tmphdl],tmp
	movelong 	P_LastObj,[tmp]
DidInsert	brl 	objloop

; Object successfully added to page data structure. If horz. guides exists then read its handle.

doneobj	lda 	hguide
	beq 	CkVguide
	call 	D_ReadHandle2,in=(fileid:w),out=(P_HorizGuides:l),err=(err)
	bcc 	GotH
	stzl 	P_HorizGuides
	brl 	killpages
GotH	rcall 	D_UnLock,in=(P_HorizGuides:ax)

; If vert. guides exist then read its handle.

CkVguide	lda 	vguide
	beq 	DidGuides
	call 	D_ReadHandle2,in=(fileid:w),out=(P_VertGuides:l),err=(err)
	bcc 	GotV
	stzl 	P_VertGuides
	brl 	killguides
GotV	rcall 	D_UnLock,in=(P_VertGuides:ax)

; Save page information. And go back for any remaining pages to be loaded.

DidGuides	call 	P_SavePage,in=(page:w)
	dec 	pagecount
	jne 	pageloop

; Verify that file is still valid by checking version numbers.

	call 	D_Read2,in=(fileid:w,#P_Header:l,#2:l),out=(ax:l),err=(err)
	jcs 	gokillgs	
	jsr	chkversion
gokillgs	jcs	killguides
	stz 	err

; File completely read in. Now repair threads so they use real handles instead of page number and
; object number offsets.

; Repair threads, part I. Loop through all objects on all pages updating their next & previous thread
; handles if they are text objects.

	addword 	P_PageCount,#2,pagecount
	moveword 	#-1,page
pageloop2	inc 	page
	call 	P_LoadPage,in=(page:w)

	stz 	count
	movelong 	P_ObjList,tmpobj
objloop2	jsl	D_BeachBall
	IsNil 	tmpobj
	jeq 	doneobj2

	rcall 	D_Deref,in=(tmpobj:ax),out=(objptr:ax)
	cmpw 	[objptr]:#P_Type,#P_ALPHA
	bne 	DidText
	call	P_Pair2Thread,in=([objptr]:#P_PThread:l),out=([objptr]:#P_PThread:l)
	call	P_Pair2Thread,in=([objptr]:#P_NThread:l),out=([objptr]:#P_NThread:l)

DidText	rcall 	D_UnLock,in=(tmpobj:ax)
	movelong 	[objptr],tmpobj
	brl 	objloop2

doneobj2	call 	P_SavePage,in=(page:w)
	dec 	pagecount
	jne 	pageloop2

; Repair threads, part II. Loop through all objects on all pages and if object is a text object then
; set the paragraph blocks for all objects in the list then rethread the object.

	addword 	P_PageCount,#2,pagecount
	moveword 	#-1,page
pageloop3	inc 	page
	call 	P_LoadPage,in=(page:w)

	stz 	count
	movelong 	P_ObjList,tmpobj
objloop3	jsl	D_BeachBall
	IsNil 	tmpobj
	jeq 	doneobj3

	rcall 	D_Deref,in=(tmpobj:ax),out=(objptr:ax)
	cmpw 	[objptr]:#P_Type,#P_ALPHA
	jne 	DidText3
	cpzl 	[objptr]:#P_PThread
	bne 	DidText3

	call 	P_SetParag,in=(objptr:l)
	call 	T_ReThread,in=(tmpobj:l)

DidText3	rcall 	D_UnLock,in=(tmpobj:ax)
	movelong 	[objptr],tmpobj
	brl 	objloop3

doneobj3	call 	P_SavePage,in=(page:w)
	dec 	pagecount
	jne 	pageloop3

; Successfully loaded file get ready to show document. Load the original page that document was saved
; on and merge guides. Then dispose of xobj.

	call 	P_ShowDoc,in=(window:l),err=(err)
	bcs 	killpages

	call 	P_LoadPage,in=(oldpage:w)
	call 	P_MergeGuides
	brl 	killxobj

; Error loading file so dispose of memory we have allocated.

killguides	lda	hguide
	beq	killvguide
	cpzl	P_HorizGuides
	beq	killvguide
	tool	_DisposeHandle,in=(P_HorizGuides:l)

killvguide	lda	vguide
	beq	killpages
	cpzl	P_VertGuides
	beq	killpages
	tool	_DisposeHandle,in=(P_VertGuides:l)

killpages	call 	P_SavePage,in=(page:w)
riploop	jsl	D_BeachBall
	call 	P_KillPage,in=(lastpage:w,#1:w)
	dec 	lastpage
	bpl 	riploop
killparray	tool	_DisposeHandle,in=(P_PageArray:l)

; Error loading file so dispose of memory we have allocated.

killrefcon	tool	_DisposeHandle,in=(RefCon:l)
killxobj	tool 	_DisposeHandle,in=(xobjhdl:l)

; If paragraph handle exists in the temp object make sure to dispose of it too.

	cpzl	xhdl
	beq	exit
	cmpw	xtype,#P_Picture
	beq	killpict
	call	X_DisposeThread,in=(xhdl:l,#1:w)
	bra	exit
killpict	tool	_KillPicture,in=(xhdl:l)

; Restore old window before returning.

exit	call 	P_LoadWindowStuff,in=(oldwin:l)

	return
;...................................................................................................;
; Check if PL id present in file if not then this file must be damaged or not a real PL file. Since
; we are handling the error return -1 to the driver.

chkversion	moveword 	#-1,err
	lda 	P_Version
	xba
	and 	#$FF
	cmp 	#P_ID
	beq 	CkVers1
	ldx	#2
	bra	doalert

; Warn user that this version of file cannot be loaded.

CkVers1	lda 	P_Version
	and 	#$FF
	cmp 	#P_VNum
	beq 	chkvok
	ldx	#3
doalert	call 	P_ErrorAlert,in=(x:w)
	sec
	rts
chkvok	clc
	rts
;...................................................................................................;

	ENDP

;---------------------------------------------------------------------------
;
; P_SaveDoc (Fileid:w,Winptr:l,BogusType:w)
;

P_SaveDoc	PROC	EXPORT
	;Using P_Data
	;Using P_ObjData
	;Using D_IOData
	;Using D_GlobalData

	input fileid:w,winptr:l,bogusType:w
	local oldwin:l,oldpage:w
	local xobjhdl:l,xobj:l,pagecount:w,page:w
	local tmpobj:l,objptr:l
	; these 4 variables are clustered. Do not rearrange or seperate.
	local templ:w,hguide:w,vguide:w,count:w
	;
	error err


	begin +b

	call D_SetCursor,in=(#WatchCursor:w)

	cmpl WinPtr,P_CurrentWin
	beq Active
	inc P_NotActive
Active

	movelong P_CurrentWin,oldwin
	call P_LoadWindowStuff,in=(winptr:l)

	moveword P_Page,oldpage
	call P_SavePage,in=(P_Page:w)

	moveword #P_VersNum,P_Version

	call D_Write2,in=(fileid:w,#P_Header:l,#P_HeaderSize:l),out=(ax:l),err=(err)
	jcs Whoops			;**

	; ;************************************
	; Traverse the document, page by page
	; object by object

	call D_NeedHand,in=(#P_TextObjSize:l),out=(xobjhdl:l),err=(err)
	jcs Whoops			;**

	rcall D_Deref,in=(xobjhdl:ax),out=(xobj:ax)


	addword P_PageCount,#2,pagecount
	moveword #-1,page
pageloop	inc page
	call P_LoadPage,in=(page:w)

	stz count
	movelong P_ObjList,tmpobj

countloop	IsNil tmpobj
	beq gotcount
	inc count
	movelong [tmpobj],objptr
	movelong [objptr],tmpobj
	bra countloop

gotcount	moveword P_VertGCount,vguide
	moveword P_HorizGCount,hguide
	moveword P_UseTemplate,templ

	call D_Write2,in=(fileid:w,!count:l,#8:l),out=(ax:l),err=(err)
	jcs Whoops			;**

	movelong P_ObjList,tmpobj
objloop	IsNil tmpobj
	jeq doneobj

	rcall D_Deref,in=(tmpobj:ax),out=(objptr:ax)

	cmpw [objptr]:#P_Type,#P_ALPHA
	jne CkPict

	tool _BlockMove,in=(objptr:l,xobj:l,#P_TextObjSize:l)

	in [xobj]:#P_PThread:l
	out [xobj]:#P_PThread:l
	xcall P_Thread2Pair

	in [xobj]:#P_NThread:l
	out [xobj]:#P_NThread:l
	xcall P_Thread2Pair

	call D_Write2,in=(fileid:w,xobj:l,#P_TextObjSize:l),out=(ax:l),err=(err)
	jcs Whoops			;**

	moveword [objptr]:#P_PThread,a
	iny
	iny
	ora [objptr],y
	jne DidObj

	call X_WriteWPScrap,in=(fileid:w,[xobj]:#P_ParagHdl:l,#1:w),err=(err)
	jcs whoops			;**

	brl DidObj

CkPict	cmp #P_PICTURE
	bne NormObj

	call D_Write2,in=(fileid:w,objptr:l,#P_TextObjSize:l),out=(ax:l),err=(err)
	jcs Whoops			;**

	call D_WriteHandle2,in=(fileid:w,[objptr]:#P_PictHandle:l),err=(err)
	jcs Whoops			;**

	pushlong [objptr]:#P_PictHandle
	_HUnlock

	bra DidObj

NormObj
	call D_Write2,in=(fileid:w,objptr:l,#P_TextObjSize:l),out=(ax:l),err=(err)
	jcs Whoops			;**

DidObj	rcall D_UnLock,in=(tmpobj:ax)
	movelong [objptr],tmpobj
	brl objloop

doneobj

	lda hguide
	beq CkVguide
	call D_WriteHandle2,in=(fileid:w,P_HorizGuides:l),err=(err)
	jcs Whoops			;**

	rcall D_UnLock,in=(P_HorizGuides:ax)


CkVguide
	lda vguide
	beq DidGuides
	call D_WriteHandle2,in=(fileid:w,P_VertGuides:l),err=(err)
	jcs Whoops			;**

	rcall D_UnLock,in=(P_VertGuides:ax)

DidGuides	dec pagecount
	jne pageloop

	call D_Write2,in=(fileid:w,#P_Header:l,#2:l),out=(ax:l),err=(err)
;	bcs Whoops			;**
;
;	bra exit

whoops
	; The driver now does all file cleanup!

exit
	tool _DisposeHandle,in=(xobjhdl:l)

	call P_LoadPage,in=(oldpage:w)
	call P_LoadWindowStuff,in=(oldwin:l)
	stz P_NotActive

	return
	ENDP
	END

