22// (c) Marco Vieth, 2019
33// https://benchmarko.github.io/CPCBasic/
44//
5- /* globals cpcBasicCharset Uint8Array */
5+ /* globals cpcBasicCharset ArrayBuffer, Uint8Array */
66
77"use strict" ;
88
@@ -849,7 +849,7 @@ Controller.prototype = {
849849 iStartLine = 0 ,
850850 bPutInMemory = false ,
851851 oData ,
852- sType ;
852+ sType , aImported ;
853853
854854 if ( sInput !== null && sInput !== undefined ) {
855855 oData = this . splitMeta ( sInput ) ;
@@ -871,6 +871,10 @@ Controller.prototype = {
871871 sInput = sInput . replace ( / \x1a + $ / , "" ) ; // eslint-disable-line no-control-regex
872872 } else if ( sType === "G" ) { // Hisoft Devpac GENA3 Z80 Assember
873873 sInput = this . asmGena3Convert ( sInput ) ;
874+ } else if ( sType === "Z" ) { // ZIP file
875+ aImported = [ ] ;
876+ this . fnLoad2 ( sInput , oInFile . sName , sType , aImported ) ;
877+ sInput = "1 ' " + aImported . join ( ", " ) ; // imported files
874878 }
875879 }
876880
@@ -1906,15 +1910,147 @@ Controller.prototype = {
19061910 soundButton . innerText = sText ;
19071911 } ,
19081912
1913+
1914+ processZipFile : function ( uint8Array , name , aImported ) {
1915+ var oZip , aZipDirectory , aEntries , i , sName2 , sData2 ;
1916+
1917+ try {
1918+ oZip = new ZipFile ( uint8Array , name ) ;
1919+ } catch ( e ) {
1920+ Utils . console . error ( e ) ;
1921+ if ( e instanceof Error ) {
1922+ this . outputError ( e , true ) ;
1923+ }
1924+ }
1925+ if ( oZip ) {
1926+ aZipDirectory = oZip . getZipDirectory ( ) ;
1927+ aEntries = Object . keys ( aZipDirectory ) ;
1928+
1929+ for ( i = 0 ; i < aEntries . length ; i += 1 ) {
1930+ sName2 = aEntries [ i ] ;
1931+
1932+ if ( sName2 . startsWith ( "__MACOSX/" ) ) { // MacOS X creates some extra folder in ZIP files
1933+ Utils . console . log ( "processZipFile: Ignoring file:" , sName2 ) ;
1934+ } else {
1935+ try {
1936+ sData2 = oZip . readData ( sName2 ) ;
1937+ } catch ( e ) {
1938+ Utils . console . error ( e ) ;
1939+ if ( e instanceof Error ) { // eslint-disable-line max-depth
1940+ this . outputError ( e , true ) ;
1941+ }
1942+ }
1943+
1944+ if ( sData2 ) {
1945+ this . fnLoad2 ( sData2 , sName2 , "" , aImported , aImported ) ; // type not known but without meta
1946+ }
1947+ }
1948+ }
1949+ }
1950+ } ,
1951+
1952+ processDskFile : function ( data , sName , aImported ) {
1953+ var oDsk , oDir , aDiskFiles , i , sFileName ;
1954+
1955+ try {
1956+ oDsk = new DiskImage ( {
1957+ sData : data ,
1958+ sDiskName : sName
1959+ } ) ;
1960+ oDir = oDsk . readDirectory ( ) ;
1961+ aDiskFiles = Object . keys ( oDir ) ;
1962+ for ( i = 0 ; i < aDiskFiles . length ; i += 1 ) {
1963+ sFileName = aDiskFiles [ i ] ;
1964+ try { // eslint-disable-line max-depth
1965+ data = oDsk . readFile ( oDir [ sFileName ] ) ;
1966+ this . fnLoad2 ( data , sFileName , "cpcBasic/binary" , aImported ) ; // recursive
1967+ } catch ( e ) {
1968+ Utils . console . error ( e ) ;
1969+ this . outputError ( e , true ) ;
1970+ }
1971+ }
1972+ } catch ( e ) {
1973+ Utils . console . error ( e ) ;
1974+ this . outputError ( e , true ) ;
1975+ }
1976+ } ,
1977+
1978+ fnLoad2 : function ( data , sName , sType , aImported ) { // eslint-disable-line complexity
1979+ var reRegExpIsText = new RegExp ( / ^ \d + | ^ [ \t \r \n \x1a \x20 - \x7e ] * $ / ) , // eslint-disable-line no-control-regex
1980+ // starting with (line) number, or 7 bit ASCII characters without control codes except \x1a=EOF
1981+ isStringData = typeof data === "string" , // it is not: data instanceof Uint8Array or ArrayBuffer,
1982+ sStorageName , sMeta , iIndex , oHeader , sInfo1 ;
1983+
1984+ sStorageName = this . oVm . vmAdaptFilename ( sName , "FILE" ) ;
1985+ sStorageName = this . fnLocalStorageName ( sStorageName ) ;
1986+
1987+ if ( sType === "text/plain" ) {
1988+ oHeader = {
1989+ sType : "A" ,
1990+ iStart : 0 ,
1991+ iLength : data . length
1992+ } ;
1993+ } else {
1994+ if ( sType === "application/x-zip-compressed" || sType === "cpcBasic/binary" || sType === "Z" ) { // are we a file inside zip?
1995+ // empty
1996+ } else if ( isStringData && data . startsWith ( "data:application/octet-stream" ) ) { // e.g. "data:application/octet-stream;base64,..."
1997+ iIndex = data . indexOf ( "," ) ;
1998+ if ( iIndex >= 0 ) {
1999+ sInfo1 = data . substr ( 0 , iIndex ) ;
2000+ data = data . substr ( iIndex + 1 ) ; // remove meta prefix
2001+ if ( sInfo1 . indexOf ( "base64" ) >= 0 ) {
2002+ data = Utils . atob ( data ) ; // decode base64
2003+ }
2004+ }
2005+ }
2006+
2007+ oHeader = isStringData ? DiskImage . prototype . parseAmsdosHeader ( data ) : null ;
2008+ if ( oHeader ) {
2009+ data = data . substr ( 0x80 ) ; // remove header
2010+ } else if ( isStringData && DiskImage . prototype . testDiskIdent ( data . substr ( 0 , 8 ) ) ) { // disk image file?
2011+ this . processDskFile ( data , sName , aImported ) ;
2012+ oHeader = null ; // ignore dsk file
2013+ } else if ( sType === "Z" ) {
2014+ this . processZipFile ( isStringData ? Utils . string2Uint8Array ( data ) : data , sName , aImported ) ;
2015+ } else if ( reRegExpIsText . test ( data ) ) {
2016+ oHeader = {
2017+ sType : "A" ,
2018+ iStart : 0 ,
2019+ iLength : data . length
2020+ } ;
2021+ } else { // binary
2022+ oHeader = {
2023+ sType : "B" ,
2024+ iStart : 0 ,
2025+ iLength : data . length
2026+ } ;
2027+ }
2028+ }
2029+
2030+ if ( oHeader ) {
2031+ sMeta = this . joinMeta ( oHeader ) ;
2032+ try {
2033+ Utils . localStorage . setItem ( sStorageName , sMeta + "," + data ) ;
2034+ this . updateStorageDatabase ( "set" , sStorageName ) ;
2035+ Utils . console . log ( "fnOnLoad: file: " + sStorageName + " meta: " + sMeta + " imported" ) ;
2036+ aImported . push ( sName ) ;
2037+ } catch ( e ) { // maybe quota exceeded
2038+ Utils . console . error ( e ) ;
2039+ if ( e . name === "QuotaExceededError" ) {
2040+ e . shortMessage = sStorageName + ": Quota exceeded" ;
2041+ }
2042+ this . outputError ( e , true ) ;
2043+ }
2044+ }
2045+ } ,
2046+
2047+
19092048 // https://stackoverflow.com/questions/10261989/html5-javascript-drag-and-drop-file-from-external-window-windows-explorer
19102049 // https://www.w3.org/TR/file-upload/#dfn-filereader
19112050 fnHandleFileSelect : function ( event ) {
19122051 var aFiles = event . dataTransfer ? event . dataTransfer . files : event . target . files , // dataTransfer for drag&drop, target.files for file input
19132052 iFile = 0 ,
1914- oStorage = Utils . localStorage ,
19152053 that = this ,
1916- reRegExpIsText = new RegExp ( / ^ \d + | ^ [ \t \r \n \x1a \x20 - \x7e ] * $ / ) , // eslint-disable-line no-control-regex
1917- // starting with (line) number, or 7 bit ASCII characters without control codes except \x1a=EOF
19182054 aImported = [ ] ,
19192055 f , oReader ;
19202056
@@ -1940,7 +2076,7 @@ Controller.prototype = {
19402076 Utils . console . log ( sText ) ;
19412077 if ( f . type === "text/plain" ) {
19422078 oReader . readAsText ( f ) ;
1943- } else if ( f . type === "application/x-zip-compressed" ) {
2079+ } else if ( f . type === "application/x-zip-compressed" || f . type === "application/zip" ) { // on Mac OS it is "application/zip"
19442080 oReader . readAsArrayBuffer ( f ) ;
19452081 } else {
19462082 oReader . readAsDataURL ( f ) ;
@@ -1966,121 +2102,18 @@ Controller.prototype = {
19662102 fnReadNextFile ( ) ;
19672103 }
19682104
1969- function fnLoad2 ( sData , sName , sType ) {
1970- var sStorageName , sMeta , iIndex , oHeader ,
1971- oDsk , oDir , aDiskFiles , i , sFileName , sInfo1 ;
1972-
1973- sStorageName = that . oVm . vmAdaptFilename ( sName , "FILE" ) ;
1974- sStorageName = that . fnLocalStorageName ( sStorageName ) ;
1975-
1976- if ( sType === "text/plain" ) {
1977- oHeader = {
1978- sType : "A" ,
1979- iStart : 0 ,
1980- iLength : sData . length
1981- } ;
1982- } else {
1983- if ( sType === "application/x-zip-compressed" || sType === "cpcBasic/binary" ) { // are we a file inside zip?
1984- } else { // e.g. "data:application/octet-stream;base64,..."
1985- iIndex = sData . indexOf ( "," ) ;
1986- if ( iIndex >= 0 ) {
1987- sInfo1 = sData . substr ( 0 , iIndex ) ;
1988- sData = sData . substr ( iIndex + 1 ) ; // remove meta prefix
1989- if ( sInfo1 . indexOf ( "base64" ) >= 0 ) {
1990- sData = Utils . atob ( sData ) ; // decode base64
1991- }
1992- }
1993- }
1994-
1995- oHeader = DiskImage . prototype . parseAmsdosHeader ( sData ) ;
1996- if ( oHeader ) {
1997- sData = sData . substr ( 0x80 ) ; // remove header
1998- } else if ( reRegExpIsText . test ( sData ) ) {
1999- oHeader = {
2000- sType : "A" ,
2001- iStart : 0 ,
2002- iLength : sData . length
2003- } ;
2004- } else if ( DiskImage . prototype . testDiskIdent ( sData . substr ( 0 , 8 ) ) ) { // disk image file?
2005- try {
2006- oDsk = new DiskImage ( {
2007- sData : sData ,
2008- sDiskName : sName
2009- } ) ;
2010- oDir = oDsk . readDirectory ( ) ;
2011- aDiskFiles = Object . keys ( oDir ) ;
2012- for ( i = 0 ; i < aDiskFiles . length ; i += 1 ) {
2013- sFileName = aDiskFiles [ i ] ;
2014- try { // eslint-disable-line max-depth
2015- sData = oDsk . readFile ( oDir [ sFileName ] ) ;
2016- fnLoad2 ( sData , sFileName , "cpcBasic/binary" ) ; // recursive
2017- } catch ( e ) {
2018- Utils . console . error ( e ) ;
2019- that . outputError ( e , true ) ;
2020- }
2021- }
2022- } catch ( e ) {
2023- Utils . console . error ( e ) ;
2024- that . outputError ( e , true ) ;
2025- }
2026- oHeader = null ; // ignore dsk file
2027- } else { // binary
2028- oHeader = {
2029- sType : "B" ,
2030- iStart : 0 ,
2031- iLength : sData . length
2032- } ;
2033- }
2034- }
2035-
2036- if ( oHeader ) {
2037- sMeta = that . joinMeta ( oHeader ) ;
2038- try {
2039- oStorage . setItem ( sStorageName , sMeta + "," + sData ) ;
2040- that . updateStorageDatabase ( "set" , sStorageName ) ;
2041- Utils . console . log ( "fnOnLoad: file: " + sStorageName + " meta: " + sMeta + " imported" ) ;
2042- aImported . push ( sName ) ;
2043- } catch ( e ) { // maybe quota exceeded
2044- Utils . console . error ( e ) ;
2045- if ( e . name === "QuotaExceededError" ) {
2046- e . shortMessage = sStorageName + ": Quota exceeded" ;
2047- }
2048- that . outputError ( e , true ) ;
2049- }
2050- }
2051- }
2052-
20532105 function fnOnLoad ( evt ) {
2054- var sData = evt . target . result ,
2106+ var data = evt . target . result ,
20552107 sName = f . name ,
2056- sType = f . type ,
2057- oZip , aEntries , i ;
2108+ sType = f . type ;
20582109
2059- if ( sType === "application/x-zip-compressed" ) {
2060- try {
2061- oZip = new ZipFile ( new Uint8Array ( sData ) , sName ) ; // rather aData
2062- } catch ( e ) {
2063- Utils . console . error ( e ) ;
2064- that . outputError ( e , true ) ;
2065- }
2066- if ( oZip ) {
2067- aEntries = Object . keys ( oZip . oEntryTable ) ;
2068- for ( i = 0 ; i < aEntries . length ; i += 1 ) {
2069- sName = aEntries [ i ] ;
2070- try {
2071- sData = oZip . readData ( sName ) ;
2072- } catch ( e ) {
2073- Utils . console . error ( e ) ;
2074- that . outputError ( e , true ) ;
2075- sData = null ;
2076- }
2077- if ( sData ) {
2078- fnLoad2 ( sData , sName , sType ) ;
2079- }
2080- }
2081- }
2110+ if ( ( sType === "application/x-zip-compressed" || sType === "application/zip" ) && data instanceof ArrayBuffer ) { // on Mac OS it is "application/zip"
2111+ sType = "Z" ;
2112+ that . fnLoad2 ( new Uint8Array ( data ) , sName , sType , aImported ) ;
2113+ } else if ( typeof data === "string" ) {
2114+ that . fnLoad2 ( data , sName , sType , aImported ) ;
20822115 } else {
2083- fnLoad2 ( sData , sName , sType ) ;
2116+ Utils . console . warn ( "Error loading file" , sName , "with type" , sType , " unexpected data:" , data ) ;
20842117 }
20852118
20862119 fnReadNextFile ( ) ;
0 commit comments