=====Reading and writing arrays in files=====
//by Jon Ripley, May 2011//\\ \\ //Alternative method added by Steve Drain, Oct 2013//\\ \\ BBC BASIC for Windows does not provide a simple method of writing arrays to a file and reading them back again, doing so traditionally requires looping through the array and saving each separate item individually.\\ \\ The PROC_ReadArray and PROC_WriteArray routines below provide equivalents to PRINT# and INPUT# for arrays and are faster than the traditional method in most cases.\\ \\
PROC_WriteArray(fileHandle%, ^array())
PROC_ReadArray(fileHandle%, ^array())
\\ When reading and writing arrays you must prefix the array variable name with the address of operator, the **^** symbol, this allows us to pass all types of array to a single routine rather than requiring a separate routine for each.\\ \\ **PROC_WriteArray** writes the contents of an array to the file specified by fileHandle%.\\ \\
DIM array(9, 9, 9, 9)
REM Initialise array
fileHandle% = OPENOUT("data.dat")
PROC_WriteArray(fileHandle%, ^array())
CLOSE#fileHandle%
\\ **PROC_ReadArray** reads data from the file specified by fileHandle% and stores it in the specified array.\\ \\
DIM array(1000, 10)
fileHandle% = OPENIN("data.dat")
PROC_ReadArray(fileHandle%, ^array())
CLOSE#fileHandle%
\\ For more information about **PROC_SwapMemory** see [[/swapping%20the%20contents%20of%20two%20areas%20of%20memory|swapping the contents of two areas of memory]].\\ \\ The full code for PROC_ReadArray, PROC_WriteArray and PROC_SwapMemory is as follows:\\ \\
DEF PROC_WriteArray(file%, parr%):LOCAL write%:write%=TRUE
DEF PROC_ReadArray(file%, parr%):LOCAL write%
LOCAL i%, tele%, esize%, temp%
IF NOT (parr%?-1 = 0 AND parr%?-2 = 40) THEN
ERROR 6, "Type mismatch: Bad array pointer"
ENDPROC
ENDIF
tele%=1
FOR i%=0 TO ?!parr%-1
tele%*=!((!parr%+1)+(4*i%))
NEXT i%
CASE parr%?-3 OF
WHEN ASC"%":esize%=4
WHEN ASC"#":esize%=8
WHEN ASC"$":esize%=6
WHEN ASC"&":esize%=1
OTHERWISE:esize%=5
ENDCASE
PTR#file%=PTR#file%
IF parr%?-3 <> ASC"$" THEN
IF write% THEN
SYS "WriteFile", @hfile%(file%), !parr%+1+?!parr%*4, tele%*esize%, ^temp%, 0
ELSE
SYS "ReadFile", @hfile%(file%), !parr%+1+?!parr%*4, tele%*esize%, ^temp%, 0
ENDIF
ELSE
LOCAL temp$()
DIM temp$(tele%-1)
PROC_SwapMemory(!^temp$()+1+?!^temp$()*4, !parr%+1+?!parr%*4, tele%*esize%)
IF write% THEN
FOR i%=0 TO tele%-1:PRINT#file%, temp$(i%):NEXT i%
ELSE
FOR i%=0 TO tele%-1:INPUT#file%, temp$(i%):NEXT i%
ENDIF
PROC_SwapMemory(!^temp$()+1+?!^temp$()*4, !parr%+1+?!parr%*4, tele%*esize%)
ENDIF
ENDPROC
REM PROC_SwapMemory(addr1%, addr2%, size%)
REM Suggested by Richard Russell
DEF PROC_SwapMemory(B%, D%, C%)
PRIVATE memswap
IF memswap=0 THEN
LOCAL P%
DIM memswap 10
P%=memswap
[OPT 2
mov al,[ebx]
xchg al,[edx]
mov [ebx],al
inc ebx
inc edx
loop memswap
ret
]
ENDIF
CALL memswap
ENDPROC
\\ Thanks to Richard Russell for the SwapMemory function.\\
----
==== Alternative method ====
\\ This method writes information about the type of array and its size in the file and checks that it it the same when reading. It does not require the SwapMemory assembler code.\\ \\ The API is the same as the routines above, except, when reading:\\
* the number of elements in the target array must be equal to the file array, but the array can be a different shape
* a string target array must be empty
\\
DEF PROC_WriteArray(file%,pntr%)
LOCAL type%,size%,numb%,temp%
PROC_ArrayData
BPUT#file%,type%
REM write the number of elements as a word
BPUT#file%,numb%
BPUT#file%,numb%>>8
BPUT#file%,numb%>>16
BPUT#file%,numb%>>24
REM flush the buffer
PTR#file%=PTR#file%
IF type%=ASC"$" THEN
FOR pntr%=pntr% TO pntr%+numb%*size%-1 STEP size%
BPUT#file%,pntr%?4
BPUT#file%,pntr%?5
size%=pntr%?4+(pntr%?5<<8)
PTR#file%=PTR#file%
SYS"WriteFile", @hfile%(file%),pntr%,size%,^temp%,0
NEXT pntr%
ELSE
SYS"WriteFile", @hfile%(file%),pntr%,numb%*size%,^temp%,0
ENDIF
ENDPROC
DEF PROC_ReadArray(file%,pntr%)
LOCAL type%,size%,numb%,temp%
PROC_ArrayData
IF type%<>BGET#file% THEN ERROR 6,"Wrong array type"
REM read the number of elements as a word
temp%=BGET#file%
temp%+=BGET#file%<<8
temp%+=BGET#file%<<16
temp%+=BGET#file%<<24
IF numb%<>temp% THEN ERROR 6,"Wrong array size"
REM flush the buffer
PTR#file%=PTR#file%
IF type%=ASC"$" THEN
FOR pntr%=pntr% TO pntr%+numb%*size%-1 STEP size%
IF pntr%?4 OR pntr%?5 THEN ERROR 6,"String array not empty"
pntr%?4=BGET#file%
pntr%?5=BGET#file%
size%=pntr%?4+(pntr%?5<<8)
DIM !pntr% size%
PTR#file%=PTR#file%
SYS"ReadFile", @hfile%(file%),pntr%,size%,^temp%,0
NEXT pntr%
ELSE
SYS"ReadFile",@hfile%(file%),pntr%,numb%*size%,^temp%,0
ENDIF
ENDPROC
DEF PROC_ArrayData
IF pntr%?-2<>ASC"(" THEN ERROR 6,"Not an array"
type%=pntr%?-3
CASE type% OF
WHEN ASC"%":size%=4
WHEN ASC"#":size%=8
WHEN ASC"$":size%=6
WHEN ASC"&":size%=1
OTHERWISE:type%=ASC"|":size%=5
ENDCASE
pntr%=!pntr%
numb%=1
FOR temp%=pntr%+1 TO pntr%+?pntr%*4 STEP 4
numb%*=!temp%
NEXT temp%
pntr%=temp%
ENDPROC