**
**     $VER: SavePicture.a V1.00 (06-12-95)
**
**     Author:  Gerben Venekamp
**     Updates: 06-12-95  Version 1.00     Initial Module
**
**  SavePicture.a save a screen buffer to a file. This can be done
**  in a number of ways. Possible types of saving: Interleave, RAW
**  Copper and Font8.
**

		include	graphics/view.i
		
		
ExtensionSize	equ	6	// Changes should be made to IFFConverter.h too

		
		XDEF	_SaveInterleave
		XDEF	_SaveRaw
		XDEF	_SaveCopperAGA
		XDEF	_SaveCopperECS
		XDEF	_SaveCopper32bit
		XDEF	_SaveCopper4bit
		XDEF	_SaveFont8
		
		XREF	_DOSBase
		XREF	_PicDepth
		
		include	exec/types.i
		include	exec/strings.i
		include	dos/dos.i
		include	graphics/gfx.i

		include	dos_lib.i
		
		include	Gadgets_enum.i
		

		STRUCTURE Save_Buffer_struct,0
		   APTR   Buffer
		   ULONG  BufferSize
		   ULONG  BufferLeft
		   BPTR   FileHeader


* Macro for adding text to an ASCII file

AddText		MACRO
		lea	(\1,PC),a5	Set start of text.
		IFLT	(\2-1<<7)	See if text fits in a 'moveq'.
		moveq	#\2,d7		Set text size.
		ELSE			Text size needs to be a longword.
		move.l	#\2,d7		Set text size.
		ENDC
		
		jsr	WriteBuffer	Write text to a buffer.
		ENDM
		
*--------------------------------------------------------------------
*
*  To call a library function use LIBCALL. If A6 is correct, just
*  use LIBCALL with the functionname only. If A6 isn't correct,
*  than use LIBCALL with the functionname AND the library offset.
*
*--------------------------------------------------------------------

LIBCALL		MACRO
		IFGT	NARG-2		More Then Two Arguments?
		FAIL			Too many Arguments!!!
		ENDC
		IFEQ	NARG-2		Two Arguments exactly.
		move.l	(_\2),a6
		ENDC
		jsr	(_LVO\1,a6)
		ENDM
		

*-------------------------------------------------------------------------
*
*  SaveInterleave(BitMap, BitPlanePtrs, IFFClip, SaveBuffer)
*                 A0      A1            A2       A3
*
*     Save a screen buffer to a file. If a 'SaveBuffer' exists, use it to
*     store the data temporary. If it doesn't, save the screen buffer
*     directly to 'SaveName'. 'SaveName' should be a valid name, because
*     no checking is done to see if 'SaveName' exists or not.
*
*  pre:  A0 - Pointer to the BitMap structure of the screen to be saved.
*        A1 - Pointer to an allocted block of memory. This will be filled
*             with the appropiate pointer values to the bitmaps. The size
*             of this block should be at least: bm_Depth × 4 bytes.
*        A2 - Pointer to a structure containing all the necessary clipping
*             values.
*        A3 - Pointer to a buffer structure.
*  post: None.
* 
*-------------------------------------------------------------------------

_SaveInterleave
		SECTION	SaveInterleave,code

		movem.l	a4-5,-(SP)
		
		move.l	a1,a4
		lea	(bm_Planes,a0),a5		Copy the bitplane pointers, because
		move.w	(bm_Depth,a0),d0		we will touch them.
		subq.w	#1,d0
CopyBitplanePtr
		move.l	(a5)+,(a4)+
		dbf	d0,CopyBitplanePtr
		
		movem.l	(SP)+,a4-5
		
		rts


		SECTION	SaveRaw,code
_SaveRaw
		movem.l	a4-5,-(SP)
		
		move.l	a1,a4
		lea	(bm_Planes,a0),a5		Copy the bitplane pointers, because
		move.w	(bm_Depth,a0),d0		we will touch them.
		subq.w	#1,d0
CpBitplanePtr
		move.l	(a5)+,(a4)+
		dbf	d0,CpBitplanePtr
		
		movem.l	(SP)+,a4-5

		rts


*-------------------------------------------------------------------------
*
*  SaveCopper(ColourMap, SaveBuffer )
*             A2         A3
*
*     Save a colourmap to a file. The name of the file can be found in
*     'SaveBuffer'. Since there are currently two distingt chipsets,
*     there are different ways to build a colourmap. One for the ECS and
*     one for AGA. Secondly a colourmap colould be directly implemented
*     as a CopperList, or just the colour data itself and the programmer
*     takes care of the building the CopperList himself. Anyway, this
*     leaves us with four possible way of saving a colourmap. We could
*     ask the user to specify how to save the colourmap or take awy the
*     burden and save them all at once. Since a colourmap does not take
*     up much space, the later methos is prefered. This way, the user
*     will always have the right colourmap.
*     The four ways a colourmap is saved are:
*        1 - AGA             extension will be .AGA
*        2 - ECS             extension will be .ECS
*        3 - 32 bit colours  extension will be .32bit
*        4 -  4 bit colours  extension will be .4bit
*
*  pre:  A2 - Pointer to the ColourMap of the screen to be saved.
*             The structure is the same as for 'LoadRGB32' colourmaps.
*        A3 - Pointer to a buffer structure.
*        A4 - SAS C Stack offset.
*  post: None.
* 
*-------------------------------------------------------------------------
		SECTION	SaveCopper
		
* Code can be found after data!

		
		ENUM
		  EITEM	dctype_Word
		  EITEM	dctype_Long

AGAHeader	dc.b	'**',LF
		dc.b	'**     $VER: AGA CopperList',LF
		dc.b	'**',LF
		dc.b	'**     Author: Gerben Venekamp',LF
		dc.b	'**',LF
		dc.b	'**  This is an AGA CopperList made with the IFFConverter.',LF
		dc.b	'**  Just insert this at the appropiate place inside your',LF
		dc.b	'**  Copper List.',LF
		dc.b	'**',LF,LF
ASCII_OrMask	dc.b	'BPLCON3OrMask'
ASCII_OrMaskSize	equ	*-ASCII_OrMask
		dc.b	'	equ	$'
BPLCON3OrMask	dc.b	'0000'
		dc.b	LF
AGAHeaderSize	equ	*-AGAHeader
		
ECSHeader	dc.b	'**',LF
		dc.b	'**     $VER: ECS CopperList',LF
		dc.b	'**',LF
		dc.b	'**     Author: Gerben Venekamp',LF
		dc.b	'**',LF
		dc.b	'**  This is an ECS CopperList made with the IFFConverter.',LF
		dc.b	'**  Just insert this at the appropiate place inside your',LF
		dc.b	'**  Copper List.',LF
		dc.b	'**',LF
ECSHeaderSize	equ	*-ECSHeader

bit32Header	dc.b	'**',LF
		dc.b	'**     $VER: 32 bit Colour map',LF
		dc.b	'**',LF
		dc.b	'**     Author: Gerben Venekamp',LF
		dc.b	'**',LF
		dc.b	'**  This is an 32 bit colourmap made with the IFFConverter.',LF
		dc.b	'**  See autodocs/graphics/LoadRGB32 for the structure explaination.',LF
		dc.b	'**  You could pass it on to LoadRGB32.',LF
		dc.b	'**',LF
bit32HeaderSize	equ	*-bit32Header

bit4Header	dc.b	'**',LF
		dc.b	'**     $VER: 4 bit Colour map',LF
		dc.b	'**',LF
		dc.b	'**     Author: Gerben Venekamp',LF
		dc.b	'**',LF
		dc.b	'**  This is an 4 bit colourmap made with the IFFConverter.',LF
		dc.b	'**  Each word represents a total RGB colour. Each colour compentent',LF
		dc.b	'**  is a 4 bit value.',LF
		dc.b	'**',LF
bit4HeaderSize	equ	*-bit4Header


ASCII_TAB	dc.b	'	'
TABSize		equ	*-ASCII_TAB
ASCII_LF		dc.b	LF
LFSize		equ	*-ASCII_LF
Line_dcw		dc.b	'dc.w'
Line_dcw_Length	equ	*-Line_dcw
Line_dcl		dc.b	'dc.l'
Line_dcl_Length	equ	*-Line_dcl
ColourPart	dc.b	'color+'
ColourPartLength	equ	*-ColourPart
ColourOffset	dc.b	'00'
ColourOffsetSize	equ	*-ColourOffset
ColourValue	dc.b	'000'
ColourValueSize	equ	*-ColourValue
ColourBank	dc.b	'bplcon3,$'
ColourBankSize	equ	*-ColourBank
BPLCON3Value	dc.b	'0000'
BPLCON3ValueSize	equ	*-BPLCON3Value
ColourValue24	dc.b	'000000'
ColourValue24Size	equ	*-ColourValue24
Comma		dc.b	','
CommaLength	equ	*-Comma
CommaStr		dc.b	',$'
CommaStrLength	equ	*-CommaStr
ASCII_Str	dc.b	'$'
ASCII_StrSize	equ	*-ASCII_Str
ASCII_Or		dc.b	'|'
ASCII_OrSize	equ	*-ASCII_Or
		CNOP	0,2

ColourCount	dc.w	0
BPLCON3Extra	dc.w	0

bplcon3_LOCTbit	equ	9

**
**  Here's where the fun begins, pure code.
**
_SaveCopperAGA
		movem.l	d2-3/d5-7/a5-6,-(SP)	Store affected non scratch registers
		
		clr.w	(BPLCON3Extra)
		AddText	AGAHeader,AGAHeaderSize
		move.l	a2,-(SP)
DoLowNibles
		move.l	(a2)+,d6
		swap	d6
		move.w	d6,(ColourCount)
		subq.w	#1,d6
		move.w	d6,d5			For Later use
		lsr.w	#5,d6
		bpl.s	MakeAGA
		moveq	#0,d6
MakeAGA
		jsr	AddColourBank
		and.w	#$1f,d5
MakeAGA.1
		move.w	#dctype_Word,d0
		bsr	AddStartLine
		
		cmp.w	(_NumberOfItems1),d5
		blt.s	CorrectItems
		move.w	(_NumberOfItems1),d4
		subq.w	#1,d4
		bra.s	RightItems
CorrectItems		
		move.w	d5,d4
RightItems
		sub.w	d4,d5
MakeAGA.2
		lea	(ColourOffset,PC),a0
		move.w	(ColourCount),d0
		subq.w	#1,d0
		sub.w	d5,d0
		sub.w	d4,d0
		add.w	d0,d0
		and.w	#$3f,d0
		moveq	#ColourOffsetSize,d1
		jsr	MakeDecimal

		cmp.w	#1<<bplcon3_LOCTbit,(BPLCON3Extra)
		bne.s	BPLCON3_LowNible
		
		move.l	(a2)+,d0			Get Red component
		and.l	#$f0000000,d0
		moveq	#20,d2
		lsr.w	d2,d0			Make R
		move.l	(a2)+,d1			Get Green component
		and.l	#$f0000000,d1
		moveq	#24,d2
		lsr.w	d2,d1
		or.w	d1,d0			Make RG
		move.l	(a2)+,d1			Get Blue component
		and.l	#$f0000000,d1
		moveq	#28,d2
		lsr.l	d2,d1
		or.w	d1,d0			Make RGB
		bra.s	GotColourComponent

BPLCON3_LowNible
;  If not High Nibke that it must be Low Nible
		move.l	(a2)+,d0			Get Red component
		and.l	#$0f000000,d0
		swap	d0			Make R
		move.l	(a2)+,d1			Get Green component
		and.l	#$0f000000,d1
		moveq	#20,d2
		lsr.l	d2,d1
		or.w	d1,d0			Make RG
		move.l	(a2)+,d1			Get Blue component
		and.l	#$0f000000,d1
		moveq	#24,d2
		lsr.l	d2,d1
		or.w	d1,d0			Make RGB
		
GotColourComponent		
		moveq	#ColourValueSize,d1
		lea	(ColourValue,PC),a0
		jsr	MakeHex
		
		AddText	ColourPart,ColourPartLength
		AddText	ColourOffset,ColourOffsetSize
		AddText	CommaStr,CommaStrLength
		AddText	ColourValue,ColourValueSize
		tst.w	d4
		beq.s	AGASkipComma
		AddText	Comma,CommaLength
AGASkipComma
		
		dbf	d4,MakeAGA.2
		
		dbf	d5,MakeAGA.1
		
		dbf	d6,MakeAGA

**
**  Okay, a very big NOTE here!!!
**  This piece of code works fine, but *any* extentions might cause
**  problems!!!! BE AWARE OF THIS THREAD!!!!
**
**  What happens is, you need to write the same copperlist twice!
**  This is due to the fact that you write the high nible of each
**  of your 24 bit colour components first, than the low nibles.
**  So, $123456 will be $135 followed by $246.
**  A special bit takes care of the nibles. So using this nible it's
**  an easy test to check when to stop. Any changes could upset this
**  easy rule.
**
		move.l	(SP),A2
		tst.w	(BPLCON3Extra,PC)
		bne.s	EndOfAGA
		or.w	#1<<bplcon3_LOCTbit,(BPLCON3Extra)
		bra	DoLowNibles	
EndOfAGA		
		jsr	FlushBuffer
		
		addq.l	#4,SP
		movem.l	(SP)+,d2-3/d5-7/a5-6	Restore affected non scratch registers
		rts


_SaveCopperECS
		movem.l	d2-3/d6-7/a5-6,-(SP)	Store affected non scratch registers

		AddText	ECSHeader,ECSHeaderSize
		
		jsr	FlushBuffer
		
		movem.l	(SP)+,d2-3/d6-7/a5-6	Restore affected non scratch registers
		rts



_SaveCopper32bit
		movem.l	d2-3/d6-7/a5-6,-(SP)	Store affected non scratch registers

		AddText	bit32Header,bit32HeaderSize
		
		moveq	#dctype_Word,d0
		jsr	AddStartLine
		
		move.l	(a2)+,d5
		lea	(BPLCON3Value,PC),a0
		move.w	d5,d0
		moveq	#4,d1
		jsr	MakeHex
		
		AddText	ASCII_Str,ASCII_StrSize
		AddText	BPLCON3Value,BPLCON3ValueSize
		AddText	CommaStr,CommaStrLength
		swap	d5
		lea	(BPLCON3Value,PC),a0
		move.w	d5,d0
		moveq	#4,d1
		jsr	MakeHex
		AddText	BPLCON3Value,BPLCON3ValueSize
		
		move.w	d5,d6			Multiply by three
		add.w	d6,d6			Multiply by three
		add.w	d5,d6			Multiply by three
		subq.w	#1,d6
Make32bit
		moveq	#dctype_Long,d0
		jsr	AddStartLine
		
		cmp.w	(_NumberOfItems2),d6
		blt.s	CorrectItems32bit
		move.w	(_NumberOfItems2),d5
		subq.w	#1,d5
		bra.s	RightItems32bit
CorrectItems32bit
		move.w	d6,d5
RightItems32bit
		sub.w	d5,d6
Make32bit.2		
		move.l	(a2)+,d0
		moveq	#6,d1
		lea	(ColourValue24,PC),a0
		jsr	MakeHex
		
		AddText	ASCII_Str,ASCII_StrSize
		AddText	ColourValue24,ColourValue24Size
		tst.w	d5
		beq.s	bit32SkipComma
		AddText	Comma,CommaLength
bit32SkipComma
		
		dbf	d5,Make32bit.2
		
		dbf	d6,Make32bit
		
		jsr	FlushBuffer
		
		movem.l	(SP)+,d2-3/d6-7/a5-6	Restore affected non scratch registers
		rts


_SaveCopper4bit
		movem.l	d2-3/d6-7/a5-6,-(SP)	Store affected non scratch registers

		AddText	bit4Header,bit4HeaderSize
		
		jsr	FlushBuffer
		
		movem.l	(SP)+,d2-3/d6-7/a5-6	Restore affected non scratch registers
		rts


*------------------------------------------------------------------------
*
*     AddColourBank
*
*------------------------------------------------------------------------
AddColourBank
		bsr	AddStartLine
		AddText	ColourBank,ColourBankSize
		move.w	(_PicDepth,a4),d0
		subq.w	#1,d0
		sub.w	d6,d0
		ror.w	#3,d0
		or.w	(BPLCON3Extra,PC),d0	Add Extra bits to BPLCON3
		moveq	#BPLCON3ValueSize,d1
		lea	(BPLCON3Value,PC),a0
		jsr	MakeHex
		
		AddText	BPLCON3Value,BPLCON3ValueSize
		AddText	ASCII_Or,ASCII_OrSize
		AddText	ASCII_OrMask,ASCII_OrMaskSize
		
		rts


*------------------------------------------------------------------------
*
*     AddStartLine( SaveBuffer, DC_Type )
*                   A3          D0
*
*  Adds the beginning of a line to a save buffer. The beginning of a
*  line look like this: <TAB1>dc.w<TAB2>
*
*  pre:  _TAB1 - Number of tabs at the beginning of a line.
*        _TAB2 - Number of tabs after the 'dc.w' statement.
*        D0 - Type of dc statement. Accepted types are: dctype_long
*                                                       dctype_word
*        A3 - Pointer to a savebuffer structure.
*  post: None.
*
*     Uneffected registers:
*
*------------------------------------------------------------------------
AddStartLine
		movem.l	d6/d0,-(SP)
		
		AddText	ASCII_LF,LFSize
		move.w	(_TAB1),d6
		jsr	AddTabs
		move.l	(SP)+,d0
		cmp.w	#dctype_Long,d0
		beq.s	Make_dc_Long
		AddText	Line_dcw,Line_dcw_Length
		bra.s	AddSecondTAB
Make_dc_Long
		AddText	Line_dcl,Line_dcl_Length
AddSecondTAB
		move.w	(_TAB2),d6
		jsr	AddTabs
		
		move.l	(SP)+,d6
		rts
		
*------------------------------------------------------------------------
*
*     AddTabs( Tabs, SaveBuffer )
*              D6    A3
*
*  Adds a series of tabs to SaveBuffer.
*
*  pre:  D6 - Number of tabs to add.
*        A3 - Pointer to a buffer structure.
*  post: None.
*
*------------------------------------------------------------------------
AddTabs
		subq.w	#1,d6
WriteTabs
		AddText	ASCII_TAB,TABSize
		dbf	d6,WriteTabs
		
		rts
**
**  Variables.
**		
		SECTION	__MERGED,data
		XDEF	_TAB1
		XDEF	_TAB2
		XDEF	_NumberOfItems1
		XDEF	_NumberOfItems2
		XDEF	_BPLCON3OrMask
		
_TAB1		dc.w	2		Default Value.
_TAB2		dc.w	1		Same here.
_NumberOfItems1	dc.w	4		Same here.
_NumberOfItems2	dc.w	9		Same here.
_BPLCON3OrMask	dc.l	BPLCON3OrMask



		SECTION	SaveFont8,code
_SaveFont8
		move.l	a4,-(SP)		Save SAS C Stack pointer.
		
		move.l	(SP)+,a4		Restore SAS C Stack pointer.
		rts


*------------------------------------------------------------------------
*
*  WriteBuffer( Buffer, Data, DataSize)
*               A3      A5    D7
*
*     Writes 'Data' to a buffer. When buffer is full, the buffer will
*     be flushed and the remaining data will be written to the buffer.
*
*  pre:  A3 - Pointer to a Buffer structure.
*        A4 - SAS C Stack Pointer
*        A5 - Pointer to the data to be written to 'Buffer'
*        D7 - Size of the 'Data' buffer.
*  post: None
*
*  Uneffected registers: A1-4, D4-6
*
*------------------------------------------------------------------------

		SECTION	WriteBuffer,code
WriteBuffer
		tst.l	d7
		beq.s	DataEmpty
		
		move.l	(BufferLeft,a3),d1
		cmp.l	d1,d7
		bge.s	BufferOverflow
		move.l	d7,d2
		sub.l	d7,(BufferLeft,a3)
		bra.s	CopyDataBuffer
BufferOverflow
		move.l	d1,d2
		clr.l	(BufferLeft,a3)
CopyDataBuffer
		sub.l	d2,d7
		move.l	(BufferSize,a3),d0
		sub.l	d1,d0
		move.l	(Buffer,a3),a0
		add.l	d0,a0
CopyDataBuffer.1
		move.b	(a5)+,(a0)+
		subq.l	#1,d2
		bne.s	CopyDataBuffer.1
		
		tst.l	(BufferLeft,a3)
		bne.s	WriteBuffer
		
		bsr.s	FlushBuffer
		bra.s	WriteBuffer
DataEmpty
		rts

*------------------------------------------------------------------------
*
*  FlushBuffer( Buffer )
*               A3
*
*     Flush the buffer. If buffer is empty, no flushing will occour.
*
*  pre:  A3 - Pointer to a buffer to be flushed
*        A4 - SAS C Stack Pointer
*  post: None.
*
*------------------------------------------------------------------------

FlushBuffer
		move.l	(BufferSize,a3),d3
		sub.l	(BufferLeft,a3),d3
		beq.s	NothingInBuffer
		
		move.l	(FileHeader,a3),d1
		move.l	(Buffer,a3),d2
		move.l	(_DOSBase,a4),a6
		jsr	(_LVOWrite,a6)
		move.l	(BufferSize,a3),(BufferLeft,a3)
NothingInBuffer
		rts

*------------------------------------------------------------------------
*
*   MakeDecimal( Inter, Length, Buffer )
*                D0.W   D1.W    A0.L
*
*     Converters an integer to a decimal representation in ASCII,
*     leading zeroes are *not* removed
*
*  pre:  A0 - Pointer into which the converted integer will be stored.
*        D0 - Integer to convert. Upper 16 bits are expected to be zero.
*        D1 - Number of digits to be used.
*  post: None.
*
*------------------------------------------------------------------------

MakeDecimal
		subq.w	#1,d1
Convert_dec
		divu.w	#10,d0
		swap	d0
		add.b	#'0',d0
		move.b	d0,(a0,d1)
		clr.w	d0
		swap	d0
		dbf	d1,Convert_dec
		
		rts
		

*------------------------------------------------------------------------
*
*   MakeHex( Inter, Length, Buffer )
*            D0.L   D1.W    A0.L
*
*     Converters an integer to a hexadecimal representation in ASCII,
*     leading zeroes are *not* removed
*
*  pre:  A0 - Pointer into which the converted integer will be stored.
*        D0 - Integer to convert. Upper 16 bits are expected to be zero.
*        D1 - Number of digits to be used.
*  post: None.
*
*------------------------------------------------------------------------

MakeHex
		subq.w	#1,d1
Convert_hex
		moveq	#0,d2
		divu.l	#16,d2:d0
		cmp.w	#10,d2
		blt.s	NoAdjustment
		addq.w	#7,d2
NoAdjustment
		add.b	#'0',d2
		move.b	d2,(a0,d1)
		dbf	d1,Convert_hex
		
		rts
		