# CMM2 file manager

Compatible with:
DOS Maximite CMM MM150 MM170 MM+ MMX Picromite ArmiteL4 Armite F4 ArmiteH7 Picomite CMM2

Description:
This BAS program was developed to test support for the various file types in MMBasic on the Colour Maximite 2.

'
' File Manager.
' written to test many of the file formats encountered on the CMM2.
' TassyJim May 2020
' mouse support added Jan 2021
' JPG info bug fixed Feb 2021
OPTION EXPLICIT
OPTION DEFAULT NONE
MODE 1,8
font 1
DIM INTEGER cWth = MM.INFO(FONTWIDTH), cHt = MM.INFO(FONTHEIGHT)
DIM INTEGER columnStart(2) = (1*cWth,33*cWth,67*cWth)
DIM INTEGER infoLine = 443, helpLine = 572
DIM INTEGER maxFiles = 512
DIM fList$(maxFiles) LENGTH 128
DIM INTEGER fCol(maxFiles)
DIM INTEGER pageStart(50) ' number of pages we can 'page up' to in text view
DIM k$, info$, Type$, showTime$ ' INKEY
DIM INTEGER mf, md, mfs, cpp = 3, rpp = 30 ' max files , max filename, columns, rows/pg
DIM INTEGER perPg = cpp * rpp ' files per page
DIM INTEGER fn, oldFn ' file number, old file number
DIM INTEGER showing, refreshDue = 1
DIM INTEGER iHt, iWdth, iBits, onDisk ' image stats
DIM INTEGER mp3Hdr(13) ' MP3 file information
dim integer mouse_port, mlb, mrb, mwb
dim integer inhibit_mouse
dim statusline$ = "h -help, v -view, t text, x -HEX, i -info, r -rename, n -new dir, s -Xmodem, k -kill, q -quit"
mouse_port = mm.info(option mouse)
if mouse_port > -1 then ' we have a mouse
CONTROLLER MOUSE OPEN mouse_port
GUI CURSOR ON 0,MOUSE(x,mouse_port), MOUSE(y,mouse_port)
endif
DO
IF refreshDue = 1 THEN refreshFiles
IF refreshDue = 2 THEN refreshDisplay
IF TIME$ <> Showtime$ AND showing = 0 THEN doTime
k$ = INKEY$
IF k$ <> "" THEN doKey
checkMouse
LOOP
SUB refreshFiles
LOCAL f$
LOCAL INTEGER z
DO : LOOP UNTIL INKEY$ = ""
refreshDue = 0
mf = 2
mfs = 0
fList$(0)= " . <DIR>"
fList$(1)= " .. <DIR>"
f$ = DIR$("*", DIR)
DO WHILE f$ <> ""
fList$(mf)= " "+UCASE$(f$)+" <DIR>"
IF LEN(fList$(mf)) > mfs THEN mfs = LEN(fList$(mf))
mf = mf + 1
f$ = DIR$()
LOOP
md = mf-2
f$ = DIR$("*", FILE)
DO WHILE f$ <> ""
fList$(mf)= UCASE$(f$)
IF LEN(fList$(mf)) > mfs THEN mfs = LEN(fList$(mf))
mf = mf + 1
f$ = DIR$()
LOOP
IF mfs > 48 THEN
cpp = 1
ELSEIF mfs > 31 THEN
cpp = 2
columnStart(1) = 50*cWth
ELSE
cpp = 3
columnStart(1) = 33*cWth
columnStart(2) = 67*cWth
ENDIF
perPg = cpp * rpp
FOR z = mf TO maxFiles ' clear out any previous file names
fList$(z)= ""
NEXT z
SORT fList$() ' after we sort, the blank cells need to be moved
FOR z = 0 TO mf-1
fList$(z)= fList$(z+maxFiles+1-mf)
fCol(z) = dispCol(fList$(z))
NEXT z
FOR z = mf TO maxFiles
fList$(z)= ""
NEXT z
refreshDisplay
END SUB
SUB refreshDisplay
LOCAL f$
LOCAL INTEGER ffp, fnx, row, col, z
refreshDue = 0
inhibit_mouse = timer
CLS
COLOUR RGB(WHITE), RGB(BLACK)
RESTORE helpTxt
READ f$
text 10,1,f$
text mm.hres/2,1,CWD$+" "+str$(md)+" directories and "+str$(mf-md-2)+" files",CT,1,1,rgb(cyan)
text 750,1,"QUIT",LT,1,1,rgb(white),rgb(red)
ffp = fn - (fn MOD perpg) ' first fn on this page
FOR fnx = ffp TO mf
IF fnx > ffp + perpg - 1 THEN EXIT FOR
col = columnStart(fnx MOD cpp)
row = INT(((fnx MOD perPg)/cpp+2)*cHt)
text col,row,fList$(fnx),LT,1,1,fCol(fnx)
NEXT fnx
oldFn = fn
highLight fn
text 1, helpLine, statusline$,LT,1,1,rgb(black),rgb(white)
END SUB
SUB highLight fn AS INTEGER
LOCAL INTEGER row, col
col = columnStart(oldFn MOD cpp)
row = INT(((oldFn MOD perPg)/cpp+2)*cHt)
text col, row,fList$(oldFn),LT,1,1,fCol(oldFn),rgb(black)
IF fn > (mf-1) THEN fn = mf-1
oldFn = fn
col = columnStart(fn MOD cpp)
row = INT(((fn MOD perPg) /cpp+2)*cHt)
text col, row, fList$(fn),LT,1,1,rgb(black),fCol(fn)
' doDialog ' uncomment to clear info panel when selected file changes
END SUB
'> Main decisions here
SUB doKey
LOCAL f$, cl$
DO : LOOP UNTIL INKEY$ = ""
IF showing = 99 THEN
showing = 0
refreshDue = 1
ENDIF
SELECT CASE k$
CASE CHR$(128) ' Up
IF showing <> 1 THEN
fn = fn - cpp
IF fn < 0 THEN fn = 0
changeChosen fn
ENDIF
CASE CHR$(129) ' Down
IF showing <> 1 THEN
fn = fn + cpp
IF fn > (mf-1) THEN fn = mf-1
changeChosen fn
ENDIF
CASE CHR$(130) ' Left
IF showing <> 1 THEN
fn = fn - 1
IF fn < 0 THEN fn = 0
changeChosen fn
ENDIF
CASE CHR$(131) ' Right
IF showing <> 1 THEN
fn = fn + 1
IF fn > (mf-1) THEN fn = mf-1
changeChosen fn
ENDIF
CASE CHR$(136) ' page Up
fn = fn - perPg
IF fn < 0 THEN fn = 0
refreshDue = 2
CASE CHR$(137) ' page Down
fn = fn + perPg
IF fn > (mf-1) THEN fn = mf-1
refreshDue = 2
CASE CHR$(134) ' home
IF showing <> 1 THEN
CHDIR "\" ' root folder
refreshFiles
fn = md + 2
IF fn > (mf-1) THEN fn = mf-1
changeChosen fn
ENDIF
CASE CHR$(135) ' end
IF showing <> 1 THEN
fn = mf-1
changeChosen fn
ENDIF
CASE CHR$(13),CHR$(10)
f$ = fList$(fn)
IF fileType$(f$) = "BAS" THEN
CLS
cl$ = cwd$
IF RIGHT$(cl$,1)="/" THEN
cl$ = cl$+f$
'RUN cwd$+f$
ELSE
cl$ = cl$+"/"+f$
'RUN cwd$+"/"+f$
ENDIF
cl$ = "RUN "+CHR$(34)+cl$+CHR$(34)
doCl cl$
ELSE
showit f$
ENDIF
CASE "V", "v" ' view the file
showit fList$(fn)
CASE CHR$(27), CHR$(26) 'esc or ^Z to exit viewing
SELECT CASE showing
CASE 1 ' image
showing = 0
refreshDue = 2
CASE 2 ' text
showing = 0
refreshDue = 2
CASE 3 ' audio
PLAY STOP
showing = 0
refreshDue = 2
CASE 4 ' GIF image
LOAD GIF
showing = 0
refreshDue = 2
CASE ELSE
PLAY STOP
END SELECT
CASE "?", "I","i" ' i for info
getDetails fList$(fn)
CASE "C", "c" ' copy file
fileCopy fList$(fn)
CASE "H", "h" ' display help
showHelp
CASE "X","x" ' display file in HEX
showHEX fList$(fn)
CASE CHR$(127), "K", "k" ' delete file
delFile fList$(fn)
CASE "R","r" ' rename
fileName fList$(fn)
CASE "N","n" ' new directory
newDir
CASE "S","s" ' send using xmodem
sendX fList$(fn)
CASE "T","t" ' show as text
showTxtIf fList$(fn)
CASE "A", "a" ' accept (receive) by xmodem
receiveX
CASE "P", "p" ' print screen
SAVE IMAGE "PS"+MID$(DATE$,9,2)+MID$(DATE$,4,2)+MID$(DATE$,1,2)+MID$(TIME$,1,2)+MID$(TIME$,4,2)+".bmp"
CASE "q","Q" ' quit
SELECT CASE showing
CASE 1 ' image
showing = 0
refreshDue = 2
CASE 2 ' text
showing = 0
refreshDue = 2
CASE 3 ' audio
PLAY STOP
CASE ELSE
PLAY STOP
CLS
doEND
END SELECT
CASE ELSE
'PRINT @(1, 455, 0) ASC(k$)
END SELECT
k$ = ""
END SUB
sub checkMouse
local integer page_pos, p_col, mx, my, nFn, ffp
static integer oldmx, oldmy
if mouse_port > -1 then
IF showing = 0 or showing = 3 THEN
ffp = fn - (fn MOD perpg) ' first fn on this page
mx = MOUSE(x,mouse_port)
my = MOUSE(y,mouse_port)
GUI CURSOR mx, my
if mx<>oldmx and my<>oldmy then ' we have moved the mouse
oldmx = mx
oldmy = my
select case cpp
case 1
page_pos = int((my-28)/cHt) + 1
case 2
p_col = int(mx/mm.hres*2) + 1
page_pos = int((my-28)/cHt)*2 + p_col
case 3
p_col = int(mx/mm.hres*3) + 1
page_pos = int((my-28)/cHt)*3 + p_col
end select
nfn = ffp + page_pos - 1
if (page_pos <= perpg and page_pos > 0) or (timer-inhibit_mouse) > 2000 then
' delay to stop madly scrolling through pages
if nFn <> fn and nFn >= 0 and nFn <= mf then
fn = nFn
changeChosen fn
endif
endif
endif
endif
if MOUSE(L,mouse_port) THEN ' left mouse button
if mlb = 0 then
k$ = chr$(13)
if mx>750 and my < (cHt+2) then k$ = "Q"
dokey
endif
mlb = 1
else
mlb = 0
endif
if MOUSE(R,mouse_port) THEN
if mrb = 0 then
k$ = chr$(27)
dokey
endif
mrb = 1
else
mrb = 0
endif
if MOUSE(W,mouse_port) THEN
if mwb = 0 then
k$ = "i"
dokey
endif
mwb = 1
else
mwb = 0
endif
endif
end sub
function checkMB$()
local integer scr
if mouse_port > -1 then
scr = MOUSE(Z,mouse_port)
if scr < 0 THEN
checkMB$ = chr$(136)
elseif scr > 0 then
checkMB$ = chr$(137)
endif
if MOUSE(L,mouse_port) THEN ' left mouse button
if mlb = 0 then
checkMB$ = chr$(13)
endif
mlb = 1
else
mlb = 0
endif
if MOUSE(R,mouse_port) THEN
if mrb = 0 then
checkMB$ = chr$(27)
endif
mrb = 1
else
mrb = 0
endif
endif
end function
sub zeroWheel ' reset the mouse wheel connter to zero
local integer scr
if mouse_port > -1 then
scr = MOUSE(Z,mouse_port)
endif
end sub
SUB doEND
if mouse_port > -1 then
CONTROLLER MOUSE CLOSE mouse_port
GUI CURSOR OFF
endif
CHDIR "\"
MODE 1,8
CLS
END
END SUB
SUB doTime
LOCAL INTEGER col, row
Showtime$ = TIME$
text 680, 1, Showtime$
' put cursor back at the selected file
col = columnStart(fn MOD cpp)
row = INT(((fn MOD perPg) /cpp+2)*cHt)
' COLOUR fCol(fn)
' PRINT @(col, row, 2) "";
' COLOUR(RGB(WHITE))
END SUB
SUB changeChosen fn AS INTEGER ' check to see if we need to page up or down
IF INT(oldFn/perPg)<>INT(fn/perPg) THEN
refreshDisplay
ELSE
highLight fn
ENDIF
END SUB
SUB showit f$
LOCAL tp$
LOCAL INTEGER xOffset, yOffset
tp$ = fileType$(f$)
'print "File ";f$
SELECT CASE tp$
CASE "<DIR>"
f$ = MID$(f$,2,LEN(f$)-7)
CHDIR f$
refreshDue = 1
CASE "BAS", "INC", "TXT", "CSV"
showing = 2
showTxt f$
CASE "FNT"
showing = 1
showFont f$
CASE "MOD"
PLAY STOP
showing = 3
PLAY MODFILE f$, playEnded
CASE "MP3"
PLAY STOP
showing = 3
PLAY MP3 f$, playEnded
CASE "WAV"
PLAY STOP
showing = 3
PLAY WAV f$, playEnded
CASE "FLAC"
PLAY STOP
showing = 3
PLAY FLAC f$, playEnded
CASE "BMP"
bmpInfo f$, onDisk, iWdth, iHt, info$
CLS
xOffset = INT((800-iWdth)/2) ' centre image on screen
yOffset = INT((600-iHt)/2)
'PRINT @(8, 13, 0)f$;" "; iWdth;" x ";iHt
text 8, 13, f$+" "+str$(iWdth)+" x "+str$(iHt)
showing = 1
LOAD BMP f$, xOffset, yOffset
'PRINT @(8, 13, 0)f$;" "; iWdth;" x ";iHt
text 8, 13, f$+" "+str$(iWdth)+" x "+str$(iHt)
CASE "JPG"
jpgInfo f$, onDisk, iWdth, iHt, info$
CLS
'PRINT @(8, 13, 0)f$;" "; iWdth;" x ";iHt;" ";info$
text 8, 13, f$+" "+str$(iWdth)+" x "+str$(iHt)+" "+info$
showing = 1
IF INSTR(info$, "Progressive") THEN
doDialog 1, " Unable to view 'progressive scan' images"
showing = 99
ELSEIF isBig(iWdth,iHt) THEN
doDialog 1, STR$(iWdth)+" x "+STR$(iHt)+" is too Big for the display!!!"
showing = 99
ELSE
xOffset = INT((800-iWdth)/2) ' centre image on screen
yOffset = INT((600-iHt)/2)
LOAD JPG f$ , xOffset, yOffset
'PRINT @(8, 13, 0)f$;" "; iWdth;" x ";iHt;" ";info$
text 8, 13, f$+" "+str$(iWdth)+" x "+str$(iHt)+" "+info$
ENDIF
CASE "PNG"
pngInfo f$, onDisk, iWdth, iHt, info$
CLS
'PRINT @(8, 13, 0)f$;" "; iWdth;" x ";iHt;" ";info$
text 8, 13, f$+" "+str$(iWdth)+" x "+str$(iHt)+" "+info$
showing = 1
IF isBig(iWdth,iHt) THEN
doDialog 1, STR$(iWdth)+" x "+STR$(iHt)+" is too Big for the display!!!"
showing = 99
ELSE
IF INSTR(info$, "Palettised") > 0 THEN
doDialog 1, " Unable to view palettised images"
ELSE
xOffset = INT((800-iWdth)/2) ' centre image on screen
yOffset = INT((600-iHt)/2)
'PAUSE 1000
LOAD PNG f$, xOffset, yOffset
ENDIF
ENDIF
'PRINT @(8, 13, 0)f$;" "; iWdth;" x ";iHt;" ";info$
text 8, 13, f$+" "+str$(iWdth)+" x "+str$(iHt)+" "+info$
CASE "GIF"
gifInfo f$, onDisk, iWdth, iHt, info$
CLS
'PRINT @(8, 13, 0)f$;" "; iWdth;" x ";iHt;" ";info$
text 8, 13, f$+" "+str$(iWdth)+" x "+str$(iHt)+" "+info$
'if iBits < 0 then print " ctrl-C to stop animation"
showing = 4
IF isBig(iWdth,iHt) THEN
doDialog 1, STR$(iWdth)+" x "+STR$(iHt)+" is too Big for the display!!!"
showing = 99
ELSE
xOffset = INT((800-iWdth)/2) ' centre image on screen
yOffset = INT((600-iHt)/2)
LOAD GIF f$, xOffset, yOffset
'PRINT @(8, 13, 0)f$;" "; iWdth;" x ";iHt;" ";info$
text 8, 13, f$+" "+str$(iWdth)+" x "+str$(iHt)+" "+info$
ENDIF
CASE ELSE
IF isText(f$) = 1 THEN
showing = 2
showTxt f$
ELSEIF isText(f$) = -1 THEN
doDialog 1, " Lines too long to display."
showing = 99
ELSE
doDialog 1, " Doesn't look like a text file - use HEX viewer"
showing = 99
ENDIF
END SELECT
END SUB
SUB doCl cl$
' we can't RUN and variable so we write the file name into a one line BAS file
' then use it to RUN the program we have selected
' OPEN "a:/cmdline.bas" FOR OUTPUT AS #2
' PRINT #2, cl$
' CLOSE #2
'' PRINT cl$
'' PAUSE 100
' RUN "a:/cmdline.bas"
execute cl$
END SUB
SUB getDetails f$
LOCAL tp$
tp$ = fileType$(f$)
doDialog
SELECT CASE tp$
CASE "BAS", "TXT", "CSV", "INC"
doDialog 1, " "+f$
doDialog 2, " Date modified: "+MM.INFO(modified f$)
doDialog 3, " Size on disk: "+STR$(MM.INFO(fileSize f$))+" bytes, "+STR$(txtInfo(f$))+" lines"
CASE "JPG"
jpgInfo f$, onDisk, iWdth, iHt, info$
doDialog 1, " "+f$
doDialog 2, " Date modified: "+MM.INFO(modified f$)
doDialog 3, " Size on disk: "+STR$(onDisk,7,0)+" bytes, "+str$(iWdth)+" wide, "+str$(iHt)+" high, "+info$
IF isBig(iWdth,iHt) THEN doDialog 4, str$(iWdth)+" x "+str$(iHt)+" is too Big for the display!!!"
if iBits = -1 then doDialog 5, " Unable to view 'progressive scan' files."
case "PNG"
pngInfo f$, onDisk, iWdth, iHt, info$
doDialog 1, " "+f$
doDialog 2, " Date modified: "+MM.INFO(modified f$)
if instr(info$, "Palettised") > 0 then
doDialog 3, " Size on disk: "+STR$(onDisk,7,0)+" bytes, "+str$(iWdth)+" wide, "+str$(iHt)+" high, "+info$
doDialog 4, " Unable to view palettised images."
else
doDialog 3, " Size on disk: "+STR$(onDisk,7,0)+" bytes, "+str$(iWdth)+" wide, "+str$(iHt)+" high, "+info$
IF isBig(iWdth,iHt) THEN doDialog 4, str$(iWdth)+" x "+str$(iHt)+" is too Big for the display!!!"
endif
case "BMP"
bmpInfo f$, onDisk, iWdth, iHt, info$
doDialog 1, " "+f$
doDialog 2, " Date modified: "+MM.INFO(modified f$)
doDialog 3, " Size on disk: "+STR$(onDisk,7,0)+" bytes, "+str$(iWdth)+" wide, "+str$(iHt)+" high, "+info$
IF isBig(iWdth,iHt) THEN doDialog 4, str$(iWdth)+" x "+str$(iHt)+" is too Big for the display!!!"
case "GIF"
gifInfo f$, onDisk, iWdth, iHt, info$
doDialog 1, " "+f$
doDialog 2, " Date modified: "+MM.INFO(modified f$)
doDialog 3, " Size on disk: "+STR$(onDisk,7,0)+" bytes, "+str$(iWdth)+" wide, "+str$(iHt)+" high, "+info$
IF isBig(iWdth,iHt) THEN doDialog 4, str$(iWdth)+" x "+str$(iHt)+" is too Big for the display!!!"
CASE "MP3"
mp3Info f$
CASE "WAV"
wavInfo f$
CASE "FLAC"
flacInfo f$
CASE "<DIR>"
doDialog 1, " "+f$+" - Directory"
case "FNT"
fontInfo f$
CASE ELSE
doDialog 1, " "+f$
doDialog 2, " Date modified: "+MM.INFO(modified f$)
doDialog 3, " Size on disk: "+str$(MM.INFO(fileSize f$))+" bytes"
END SELECT
END SUB
SUB playEnded
if MM.INFO$(SOUND) <> "OFF" then
PLAY STOP
endif
showing = 0
refreshDue = 2
END SUB
FUNCTION fileType$(f$) ' returns file extension
LOCAL INTEGER p
IF RIGHT$(f$,5)="<DIR>" THEN
fileType$ = "<DIR>"
ELSE
p = INSTR(f$,".")
if p > 0 then
for p = len(f$) to 1 step -1
if mid$(f$,p,1)="." then exit for
next p
endif
IF p > 0 AND p < (LEN(f$)-1) THEN fileType$ = UCASE$(MID$(f$,p+1))
ENDIF
END FUNCTION
FUNCTION dispCol(f$) AS INTEGER ' display colour for various file types
LOCAL t$
t$ = fileType$(f$)
SELECT CASE t$
CASE "BAS", "INC"
dispCol = RGB(RED)
CASE "<DIR>"
dispCol = RGB(127,127,255)
CASE "TXT","CSV","LOG"
dispCol = RGB(GREEN)
CASE "BMP","JPG","PNG","GIF"
dispCol = RGB(CYAN)
CASE "MOD", "WAV", "FLAC", "MP3"
dispCol = RGB(YELLOW)
case "FNT"
dispCol = RGB(magenta)
CASE ELSE
dispCol = RGB(WHITE)
END SELECT
END FUNCTION
function isBig(iWdth AS INTEGER, iHt AS INTEGER) as integer
IF iWdth > mm.hres OR iHt > mm.vres THEN
isBig = 1
ELSE
isBig = 0
ENDIF
end function
sub jpgInfo f$, onDisk AS INTEGER, iWdth AS INTEGER, iHt AS INTEGER, info$
LOCAL INTEGER block, bs, n
LOCAL x$
onDisk = MM.INFO(filesize f$)
info$ = ""
OPEN f$ FOR INPUT AS #2
DO
block = ASC(INPUT$(1,#2)) ' flag for start of block
IF block = 255 THEN
block = ASC(INPUT$(1,#2)) ' block type
IF block = &hC0 THEN
x$ = INPUT$(3,#2)
iHt = ASC(INPUT$(1,#2))*256 + ASC(INPUT$(1,#2))
iWdth = ASC(INPUT$(1,#2))*256 + ASC(INPUT$(1,#2))
EXIT DO
elseif block = &hC2 THEN
info$ = "Progressive"
x$ = INPUT$(3,#2)
iHt = ASC(INPUT$(1,#2))*256 + ASC(INPUT$(1,#2))
iWdth = ASC(INPUT$(1,#2))*256 + ASC(INPUT$(1,#2))
exit do
elseif block >= &hD0 and block <= &hD9 then
' do nothing
else
bs = ASC(INPUT$(1,#2))*256 + ASC(INPUT$(1,#2))
'print hex$(block), " ",bs
if bs > 4 then
for n = 1 to bs-4
x$ = INPUT$(1,#2)
next n
endif
ENDIF
ENDIF
LOOP
CLOSE #2
END SUB
sub bmpInfo f$, onDisk AS INTEGER, iWdth AS INTEGER, iHt AS INTEGER, info$
LOCAL x$
onDisk = MM.INFO(filesize f$)
OPEN f$ FOR INPUT AS #2
x$ = INPUT$(18,#2) ' skip this
iWdth = ASC(INPUT$(1,#2))+ASC(INPUT$(1,#2))*256
iWdth = iWdth +ASC(INPUT$(1,#2))*2^16+ASC(INPUT$(1,#2))*2^24
iHt = ASC(INPUT$(1,#2))+ASC(INPUT$(1,#2))*256
iHt = iHt +ASC(INPUT$(1,#2))*2^16+ASC(INPUT$(1,#2))*2^24
x$ = INPUT$(2,#2) ' colour planes - not used
info$ = str$(ASC(INPUT$(1,#2))+ASC(INPUT$(1,#2))*256)+" bit"
CLOSE #2
END SUB
sub gifInfo f$, onDisk AS INTEGER, iWdth AS INTEGER, iHt AS INTEGER, info$
LOCAL x$, iBits as integer
onDisk = MM.INFO(filesize f$)
OPEN f$ FOR INPUT AS #2
x$ = INPUT$(6,#2) ' skip this GIF89a
iWdth = ASC(INPUT$(1,#2))+ASC(INPUT$(1,#2))*256
iHt = ASC(INPUT$(1,#2))+ASC(INPUT$(1,#2))*256
x$ = INPUT$(1,#2) ' colour table start and bits
iBits = (asc(x$) and &b00000111) + 1
'if (asc(x$) and &b10000000) then iBits = 0 - iBits ' indicate animated image
CLOSE #2
info$ = str$(iBits)+" bit"
END SUB
sub pngInfo f$, onDisk AS INTEGER, iWdth AS INTEGER, iHt AS INTEGER, info$
' data from https://en.wikipedia.org/wiki/Portable_Network_Graphics
LOCAL x$, iBits as integer
onDisk = MM.INFO(filesize f$)
OPEN f$ FOR INPUT AS #2
x$ = INPUT$(12,#2)
x$ = INPUT$(4,#2) ' should be IHDR
iWdth = ASC(INPUT$(1,#2))*2^24 + ASC(INPUT$(1,#2))*2^16
iWdth = iWdth +ASC(INPUT$(1,#2))*256 + ASC(INPUT$(1,#2))
iHt = ASC(INPUT$(1,#2))*2^24 + ASC(INPUT$(1,#2))*2^16
iHt = iHt +ASC(INPUT$(1,#2))*256 + ASC(INPUT$(1,#2))
iBits = ASC(INPUT$(1,#2)) ' colour bits/pixel
select case ASC(INPUT$(1,#2)) ' colour type
case 0
info$ = str$(iBits)+" bit Grayscale"
case 2
info$ = str$(iBits*3)+" bit Truecolour"
case 3
info$ = str$(iBits)+" bit Palettised"
case 4
info$ = str$(iBits*2)+" bit Grayscale+Alpha"
case 6
info$ = str$(iBits*4)+" bit RGBA"
case else
info$ = str$(iBits)+" bit/pixel"
end select
CLOSE #2
END SUB
sub fontInfo f$
LOCAL INTEGER onDisk, cc, cw, ch, cs, fnt
LOCAL txt$
onDisk = MM.INFO(filesize f$)
OPEN f$ FOR INPUT AS #2
DO WHILE NOT EOF(#2)
LINE INPUT #2, txt$
txt$ = ucase$(ltrim$(txt$))
if fnt = 1 then
cc = val("&h"+mid$(txt$,1,2)) ' character count
cs = val("&h"+mid$(txt$,3,2)) ' character start
ch = val("&h"+mid$(txt$,5,2)) ' character height
cw = val("&h"+mid$(txt$,7,2)) ' character width
exit do
endif
if left$(txt$,10) = "DEFINEFONT" then fnt = 1
LOOP
CLOSE #2
doDialog 1, " "+f$
doDialog 2," Date modified: "+MM.INFO(modified f$)
if fnt = 1 then
doDialog 3," "+str$(onDisk)+" bytes, "+str$(cc)+ " char(s) starting at chr$("+str$(cs)+"), "+str$(cw)+" wide x "+str$(ch)+" high"
if cs < 32 then doDialog 4, "Characters below chr$(32) are not allowed."
else
doDialog 3," "+str$(onDisk)+" bytes, Not a valid Font file."
endif
end sub
SUB flacInfo f$
' data from https://xiph.org/flac/format.html#def_STREAMINFO
LOCAL INTEGER onDisk, bs, sf, ch
LOCAL hdr$, st$
onDisk = MM.INFO(filesize f$)
OPEN f$ FOR INPUT AS #2
hdr$ = INPUT$(4,#2) ' should be "fLaC"
hdr$ = INPUT$(4,#2) ' skip this
hdr$ = INPUT$(10,#2) ' skip this
hdr$ = INPUT$(4,#2)
hdr$ = BIN$(STR2BIN(UINT32, hdr$, big),32)
sf = VAL("&B"+MID$(hdr$,1,20)) ' sample rate
ch = VAL("&B"+MID$(hdr$,21,3)) + 1 ' channels
bs = VAL("&B"+MID$(hdr$,24,5)) + 1 ' bits/sample
IF ch = 1 THEN
st$ = "Mono"
ELSEIF ch = 2 THEN
st$ = "Stereo"
ELSE
st$ = STR$(ch)+" channels"
ENDIF
CLOSE #2
doDialog 1, " "+f$
doDialog 2," Date modified: "+MM.INFO(modified f$)
doDialog 3, " "+STR$(onDisk,8,0)+" bytes, "+str$(bs)+" bits/sample, "+str$(sf)+" Hz, "+st$
END SUB
SUB wavInfo f$
' data from http://www.topherlee.com/software/pcm-tut-wavformat.html
LOCAL INTEGER onDisk, bs, sf, ch
LOCAL hdr$, st$
onDisk = MM.INFO(filesize f$)
OPEN f$ FOR INPUT AS #2
hdr$ = INPUT$(20,#2) ' skip this
hdr$ = INPUT$(2,#2) ' type of format
hdr$ = INPUT$(2,#2) ' no of channels
ch = STR2BIN(UINT16, hdr$)
IF ch = 2 THEN
st$ = "Stereo"
ELSE
st$ = "Mono"
ENDIF
hdr$ = INPUT$(4,#2) ' sample rate
sf = STR2BIN(UINT32, hdr$)
hdr$ = INPUT$(4,#2) ' sample rate * b/s * ch / 8
hdr$ = INPUT$(2,#2) ' b/s * ch / 8
hdr$ = INPUT$(2,#2) ' bits / sample
bs = STR2BIN(UINT16, hdr$)
CLOSE #2
doDialog 1, " "+f$
doDialog 2," Date modified: "+MM.INFO(modified f$)
doDialog 3," "+STR$(onDisk,8,0)+" bytes, "+str$(bs)+" bits/sample, "+str$(sf)+" Hz, "+st$
END SUB
SUB mp3Info f$
' data from http://mpgedit.org/mpgedit/mpeg_format/mpeghdr.htm
LOCAL INTEGER onDisk, vl, br, sf
LOCAL FLOAT v
LOCAL hdr$, st$
onDisk = MM.INFO(filesize f$)
OPEN f$ FOR INPUT AS #2
hdr$ = INPUT$(4,#2)
mp3Hdr(1) = STR2BIN(UINT32, hdr$, big)
hdr$ = BIN$(STR2BIN(UINT32, hdr$, big),32)
mp3Hdr(2) = 4-VAL("&B"+MID$(hdr$,12,2)) ' mpeg audio version 3=2.5
mp3Hdr(3) = 4-VAL("&B"+MID$(hdr$,14,2)) ' layer
mp3Hdr(4) = VAL("&B"+MID$(hdr$,16,1)) ' protection
mp3Hdr(5) = VAL("&B"+MID$(hdr$,17,4)) ' bitrate index
mp3Hdr(6) = VAL("&B"+MID$(hdr$,21,2)) ' sample rate index
mp3Hdr(7) = VAL("&B"+MID$(hdr$,23,1)) ' padding bit
mp3Hdr(8) = VAL("&B"+MID$(hdr$,24,1)) ' private bit
mp3Hdr(9) = VAL("&B"+MID$(hdr$,25,2)) ' channel mode
mp3Hdr(10) = VAL("&B"+MID$(hdr$,27,2)) ' mode ext
mp3Hdr(11) = VAL("&B"+MID$(hdr$,29,1)) ' copyright
mp3Hdr(12) = VAL("&B"+MID$(hdr$,30,1)) ' original
mp3Hdr(13) = VAL("&B"+MID$(hdr$,31,2)) ' emphasis
CLOSE #2
v = mp3Hdr(2)
IF v = 3 THEN v = 2.5
vl = mp3Hdr(2) * 10 + mp3Hdr(3)
SELECT CASE vl
CASE 11 ' V1,L1
br = VAL(MID$("000 32 64 96128160192224256288320352384416448999",mp3Hdr(5)*3+1,3))
CASE 12 ' V1,L2
br = VAL(MID$("000 32 48 56 64 80 96112128160192224256320384999",mp3Hdr(5)*3+1,3))
CASE 13 ' V1,L3
br = VAL(MID$("000 32 40 48 56 64 80 96112128160192224256320999",mp3Hdr(5)*3+1,3))
CASE 21,31 ' V2,L1 and V2.5,L1
br = VAL(MID$("000 32 48 56 64 80 96112128144160176192224256999",mp3Hdr(5)*3+1,3))
CASE ELSE
br = VAL(MID$("000 8 16 24 32 40 48 56 64 80 96112128144160999",mp3Hdr(5)*3+1,3))
END SELECT
sf = VAL(MID$(" 44100 48000 32000 ",mp3Hdr(6)*6+1,6))
IF mp3Hdr(2) = 2 THEN
sf = sf /2
ELSEIF mp3Hdr(2) = 3 THEN
sf = sf /4
ENDIF
IF mp3Hdr(9) = 3 THEN
st$ = "Mono"
ELSE
st$ = "Stereo"
ENDIF
doDialog 1, " "+f$
doDialog 2, " Date modified: "+MM.INFO(modified f$)
doDialog 3, " "+STR$(onDisk,8,0)+" Bytes, V"+STR$(v,1,1)+"L"+STR$(mp3Hdr(3),1,0)+", "+str$(br)+"kbps, "+str$(sf)+"Hz, "+st$
END SUB
SUB delFile f$
LOCAL k$
doDialog 1, "Do you want to delete "+f$+"? y/n "
DO
k$ = INKEY$
LOOP UNTIL k$ <>""
IF k$ = "Y" OR k$ = "y" THEN
IF fileType$(f$) = "<DIR>" THEN
IF isEmpty(f$) THEN
KILL f$
ELSE
doDialog 2, " Directory not empty!!!"
PAUSE 2000
ENDIF
ELSE
KILL f$
ENDIF
refreshDue = 1
ENDIF
END SUB
FUNCTION isEmpty(f$) AS INTEGER ' check to see if a directory is empty
LOCAL k$
IF fileType$(f$) = "<DIR>" THEN
f$ = MID$(f$,2,LEN(f$)-7)
CHDIR f$
k$ = DIR$("*", DIR)+DIR$("*", FILE)
CHDIR ".."
IF k$ = "" THEN
isEmpty = 1
ENDIF
ENDIF
END FUNCTION
FUNCTION isText(f$) AS INTEGER ' returns 1 - text , -1 no LineEnd, 0 - not text
LOCAL txt$
LOCAL INTEGER n, c
isText = 1
OPEN f$ FOR INPUT AS #2
txt$ = INPUT$(255,#2)
FOR n = 1 TO LEN(txt$)
c = ASC(MID$(txt$,n,1))
IF c > 127 OR (c > 0 AND c < 8) OR (c > 13 AND c < 32 AND c <> 27 and c <> 26) THEN
isText = 0
EXIT FOR
ENDIF
NEXT n
CLOSE #2
n = instr(txt$,chr$(10))
if n = 0 then n = instr(txt$,chr$(13))
if n = 0 then isText = 0 - isText
END FUNCTION
SUB fileName f$
LOCAL fNew$, k$, ff$
'doDialog
doDialog 1, "Enter new name for "+f$+" or blank for no change"
doDialog 2," "
print
IF fileType$(f$) = "<DIR>" THEN
ff$ = MID$(f$,2,LEN(f$)-7)
ELSE
ff$ = f$
ENDIF
INPUT fNew$
IF fNew$ <> "" THEN
k$ = DIR$(fNew$)
IF k$ = "" THEN
RENAME ff$ AS fNew$
refreshDue = 1
ELSE
doDialog 1, fNew$+" already exists!"
ENDIF
ELSE
doDialog
ENDIF
END SUB
SUB fileCopy f$
LOCAL fNew$, k$, ff$
doDialog
doDialog 1, "Enter new name for "+f$+" copy or blank to cancel"
doDialog 2," "
print
IF fileType$(f$) = "<DIR>" THEN
ff$ = MID$(f$,2,LEN(f$)-7)
ELSE
ff$ = f$
ENDIF
INPUT fNew$
IF fNew$ <> "" THEN
k$ = DIR$(fNew$)
IF k$ = "" THEN
COPY ff$ TO fNew$
DO : LOOP UNTIL INKEY$ = ""
refreshDue = 1
ELSE
doDialog 1, fNew$+" already exists!"
ENDIF
ELSE
doDialog
ENDIF
END SUB
SUB newDir
LOCAL fNew$, k$
doDialog
doDialog 1, "Enter new directory or blank for no change"
doDialog 2," "
print
INPUT fNew$
IF fNew$ <> "" THEN
k$ = DIR$(fNew$)
IF k$ = "" THEN
MKDIR fNew$
refreshDue = 1
ELSE
doDialog 1, fNew$+" already exists!"
ENDIF
ELSE
doDialog
ENDIF
END SUB
SUB sendX f$
'doDialog
doDialog 1, "Sending ";f$
on error skip 1
XMODEM SEND f$
doDialog 2, "Sent!"
END SUB
sub receiveX
LOCAL fNew$, k$, ff$
doDialog 1, "Enter file name or blank to abort"
print " ";
INPUT fNew$
k$ = DIR$(fNew$)
doDialog
IF fNew$ <> "" THEN
'PRINT SPACE$(90)
IF k$ = "" THEN
doDialog 1, "Receiving "+fNew$
XMODEM receive fNew$
doDialog 2, "Receive finished!"
pause 500
refreshFiles
fn = fileIndex(fNew$)
highlight fn
ELSE
doDialog 1, fNew$+" already exists!"
ENDIF
ENDIF
end sub
'SUB setCurrent f$ ' doesn't work from within a program
' PRINT @(1, infoLine, 0) SPACE$(80)
' IF fileType$(f$) = "BAS" THEN
' OPTION CURRENT f$
' PRINT @(1, infoLine, 0) "'Current' file now: ";f$
' ELSE
' PRINT @(1, infoLine, 0) f$; " is not a BAS file."
' ENDIF
'END SUB
sub showFont f$
LOCAL INTEGER onDisk, cc, cw, ch, cs, fnt, rows, c,x,y
local float cpr
LOCAL txt$
onDisk = MM.INFO(filesize f$)
OPEN f$ FOR INPUT AS #2
DO WHILE NOT EOF(#2)
LINE INPUT #2, txt$
txt$ = ucase$(ltrim$(txt$))
if fnt = 1 then
cc = val("&h"+mid$(txt$,1,2)) ' character count
cs = val("&h"+mid$(txt$,3,2)) ' character start
ch = val("&h"+mid$(txt$,5,2)) ' character height
cw = val("&h"+mid$(txt$,7,2)) ' character width
exit do
endif
if left$(txt$,10) = "DEFINEFONT" then fnt = 1
LOOP
close #2
cls
print f$;" ";cc; " char(s) starting at chr$(";cs;"), ";cw;" wide x ";ch;" high"
if cs < 32 then
print " Characters below chr$(32) are not allowed."
else
cpr = int((sqr(cc)*1.5+3)/4)*4 'characters per line
if cpr > cc then cpr = cc
rows = cc/cpr+1
x = (mm.hres-cpr*cw)/2 ' center font on screen
y = (mm.vres-rows*ch)/2
load font f$
font 8
print @(x,y);
for c = 0 to cc-1
if (c mod cpr) = 0 then
print @(x,y);
y = y +ch
endif
print chr$(c+cs);
next c
font 1
endif
end sub
sub showTxtIf f$ ' check text file before showing
local integer ist
ist = isText(f$)
if ist = 1 then
showing = 2
showTxt f$
elseif ist = -1 then
doDialog 1, " Lines too long to display."
showing = 99
else
doDialog 1, " Doesn't look like a text file - use HEX viewer"
showing = 99
endif
end sub
SUB showTxt f$
LOCAL INTEGER row, fSize, fpos, p
LOCAL txt$
CLS
zeroWheel
FOR p = 0 TO 50
pageStart(p) = 0
NEXT p
row = 2
OPEN f$ FOR INPUT AS #2
fSize = LOF(#2)
PRINT @(8, 13, 2) f$ ;" ";STR$(fSize,8,0);" bytes"
txt$ = INPUT$(255,#2) ' check for line feed
if instr(txt$,chr$(10)) = 0 then
print " Line feed not detected - use HEX viewer"
else
seek #2, 0
DO WHILE NOT EOF(#2)
LINE INPUT #2, txt$
PRINT @(8, row*13, 0) LEFT$(txt$,98)
row = row + 1
fpos = fpos + LEN(txt$)+1
IF LEN(txt$) > 98 THEN
PRINT @(8, row*13, 0) MID$(txt$,99,98)
row = row + 1
IF LEN(txt$) > 196 THEN
PRINT @(8, row*13, 0) MID$(txt$,197)
row = row + 1
ENDIF
ENDIF
IF row > 38 OR EOF(#2) THEN
p = (p+1) MOD 50
pageStart(p) = fpos
PRINT @(8, 43*13, 0) " Press any key to continue, PGup to go back or esc to quit..."
PRINT @(8, 13, 2) f$ ;" ";STR$(fpos,8,0);"/";STR$(fSize,8,0);" bytes"
DO
k$ = INKEY$
if k$ = "" then k$ = checkMB$()
LOOP UNTIL k$<>""
IF k$ = CHR$(27) THEN
showing = 0
refreshDue = 2
EXIT DO
ELSEIF k$ = CHR$(136) THEN ' page up
p = (p+48) MOD 50
fpos = pageStart(p) 'to the start of 2 pages back
IF fpos < 0 THEN fpos = 0
SEEK #2, fpos
CLS
row = 2
ELSEIF EOF(#2) THEN
p = (p+49) MOD 50
fpos = pageStart(p) 'to the start of last page
IF fpos < 0 THEN fpos = 0
SEEK #2, fpos
CLS
row = 2
ELSE
CLS
row = 2
ENDIF
ENDIF
LOOP
endif
CLOSE #2
END SUB
SUB showHEX f$
LOCAL INTEGER row, fSize, fpos, n
LOCAL txt$, hx$
CLS
zeroWheel
showing = 2
row = 2
OPEN f$ FOR INPUT AS #2
fSize = LOF(#2)
PRINT @(8, 13, 2) f$ ;" ";STR$(fSize,8,0);" bytes"
DO WHILE NOT EOF(#2)
txt$ = INPUT$(16, #2)
hx$=HEX$(fpos,8)+" "
FOR n = 1 TO LEN(txt$)
hx$=hx$+HEX$(ASC(MID$(txt$,n,1)),2)+" "
NEXT n
IF LEN(hx$) < 62 THEN hx$=hx$+SPACE$(62-LEN(hx$))
FOR n = 1 TO LEN(txt$)
IF ASC(MID$(txt$,n,1))>32 AND ASC(MID$(txt$,n,1)) < 127 THEN
hx$=hx$+MID$(txt$,n,1)
ELSE
hx$=hx$+"."
ENDIF
NEXT n
PRINT @(8, row*13, 0) hx$
row = row + 1
fpos = fpos + 16
IF row > 41 OR EOF(#2) THEN
PRINT @(8, helpLine, 0) " Press any key to continue, PGup to go back or esc to quit..."
PRINT @(8, 13, 2) f$ ;" ";STR$(fpos,8,0);"/";STR$(fSize,8,0);" bytes"
DO
k$ = INKEY$
if k$ = "" then k$ = checkMB$()
LOOP UNTIL k$<>""
IF k$ = CHR$(27) THEN
showing = 0
refreshDue = 2
EXIT DO
ELSEIF k$ = CHR$(136) THEN ' page up
fpos = fpos - 40*16*2 'to the start of 2 pages back
IF fpos < 0 THEN fpos = 0
SEEK #2, fpos
CLS
row = 2
ELSE
CLS
row = 2
ENDIF
ENDIF
LOOP
CLOSE #2
END SUB
FUNCTION txtInfo(f$) AS INTEGER ' count the number of lines in a text file
LOCAL txt$
OPEN f$ FOR INPUT AS #2
DO WHILE NOT EOF(#2)
LINE INPUT #2, txt$
txtInfo = txtInfo + 1
LOOP
CLOSE #2
END FUNCTION
SUB speakIt f$, sp AS INTEGER
LOCAL txt$, j$
LOCAL INTEGER q
PLAY STOP
showing = 3
OPEN f$ FOR INPUT AS #2
DO WHILE NOT EOF(#2)
LINE INPUT #2, txt$
TTS txt$, sp
DO
j$ = INKEY$
IF j$ = "Q" OR j$ = "q" OR j$ = CHR$(27) THEN q = 1
LOOP UNTIL MM.INFO$(SOUND) = "OFF" OR q = 1
IF q = 1 THEN EXIT DO
LOOP
CLOSE #2
playEnded
END SUB
SUB showHelp
LOCAL INTEGER row
local float ds, df, du
LOCAL txt$
ds = MM.INFO(disk size)/1000000 ' 1048576 used 1000000 to agree with Windows
df = MM.INFO(free SPACE)/1000000
du = ds - df
showing = 2
CLS
row = 2
RESTORE helpTxt
DO
READ txt$
PRINT @(8, row*cHt, 0) txt$
row = row + 1
LOOP UNTIL txt$ = ""
PRINT " MMBasic V";MM.INFO(VERSION);" Disk used ";STR$(du,6,1);" MB with ";STR$(df,6,1);" MB free"
PRINT ""
MEMORY
option list
END SUB
helpTxt:
DATA " File Master V 1.2"
DATA " Use arrow keys to move between files, PgUp/PgDown to change pages"
DATA " Enter to change folder"
data " Home- Change to first file in root folder"
data " End - select last file in current folder"
DATA " Enter View Image and text files, PLAY audio files"
DATA " and load and RUN BAS files"
DATA " v - view file (including BAS files)"
data " t - view as text"
DATA " x - view as HEX"
DATA " esc - quit viewing or playing."
DATA " i - display info about the file"
DATA " s - send file to PC using XMODEM"
DATA " a - accept file from PC using XMODEM"
DATA " del - permanently delete file."
DATA " k - permanently delete file."
DATA " r - rename file"
DATA " c - copy file"
DATA " n - make new directory"
DATA " h - display this help"
DATA " q - Quit"
DATA ""
sub doDialog iLine as integer, message$ ' iLine = 0 to clear all lines
static integer cleared
message$ = left$(message$,90)
if iLine = 0 then
if cleared = 0 then
BOX 1, infoline, 91*cWth, 7*cHt,1,rgb(black),rgb(black)
cleared = 1
endif
elseif iLine = 1 then
BOX 1, infoline, 91*cWth, 7*cHt,1,rgb(gray),rgb(black)
text 2, infoLine+iLine*cHt, message$+space$(90-len(message$))
cleared = 0
else
text 2, infoLine+iLine*cHt, message$+space$(90-len(message$))
cleared = 0
endif
DO : LOOP UNTIL INKEY$ = ""
end sub
FUNCTION LTrim$(a$)
LOCAL integer n
if len(a$)=0 then
LTrim$ = ""
else
FOR n= 1 TO LEN(a$)
IF MID$(a$,n,1)<>" " and MID$(a$,n,1)<>CHR$(9) THEN exit for
NEXT n
LTrim$=mid$(a$,n)
endif
END FUNCTION
function fileIndex(f$) as integer
local integer n
for n = 2 to mf
if ucase$(f$) = fList$(n) then fileIndex = n
next n
end function

Last edited: 07 February, 2021