@@ -154,6 +154,12 @@ namespace pxt {
154154 const existing = this . lookupByID ( id ) ;
155155
156156 if ( ! assetEquals ( existing , newValue ) ) {
157+ if ( ! validateAsset ( newValue ) && validateAsset ( existing ) ) {
158+ pxt . warn ( "Refusing to overwrite asset with invalid version" ) ;
159+ pxt . tickEvent ( "assets.invalidAssetOverwrite" , { assetType : newValue . type } ) ;
160+ return existing ;
161+ }
162+
157163 this . removeByID ( id ) ;
158164 asset = this . add ( newValue ) ;
159165 this . notifyListener ( newValue . internalID ) ;
@@ -1948,6 +1954,69 @@ namespace pxt {
19481954 return getShortIDCore ( asset . type , asset . id ) ;
19491955 }
19501956
1957+ export function validateAsset ( asset : pxt . Asset ) {
1958+ if ( ! asset ) return false ;
1959+
1960+ switch ( asset . type ) {
1961+ case AssetType . Image :
1962+ case AssetType . Tile :
1963+ return validateImageAsset ( asset as ProjectImage | Tile ) ;
1964+ case AssetType . Tilemap :
1965+ return validateTilemap ( asset as ProjectTilemap ) ;
1966+ case AssetType . Animation :
1967+ return validateAnimation ( asset as Animation )
1968+ case AssetType . Song :
1969+ return validateSong ( asset as Song ) ;
1970+ }
1971+ }
1972+
1973+ function validateImageAsset ( asset : ProjectImage | Tile ) {
1974+ if ( ! asset . bitmap ) return false ;
1975+
1976+ return validateBitmap ( sprite . Bitmap . fromData ( asset . bitmap ) ) ;
1977+ }
1978+
1979+ function validateTilemap ( tilemap : ProjectTilemap ) {
1980+ if (
1981+ ! tilemap . data ||
1982+ ! tilemap . data . tilemap ||
1983+ ! tilemap . data . tileset ||
1984+ ! tilemap . data . tileset . tileWidth ||
1985+ ! tilemap . data . tileset . tiles ?. length ||
1986+ ! tilemap . data . layers
1987+ ) {
1988+ return false ;
1989+ }
1990+
1991+ return validateBitmap ( sprite . Bitmap . fromData ( tilemap . data . layers ) ) &&
1992+ validateBitmap ( tilemap . data . tilemap ) ;
1993+ }
1994+
1995+ function validateAnimation ( animation : Animation ) {
1996+ if ( ! animation . frames ?. length || animation . interval <= 0 ) {
1997+ return false ;
1998+ }
1999+
2000+ return ! animation . frames . some ( frame => ! validateBitmap ( sprite . Bitmap . fromData ( frame ) ) ) ;
2001+ }
2002+
2003+ function validateBitmap ( bitmap : sprite . Bitmap ) {
2004+ return bitmap . width > 0 &&
2005+ bitmap . height > 0 &&
2006+ ! Number . isNaN ( bitmap . x0 ) &&
2007+ ! Number . isNaN ( bitmap . y0 ) &&
2008+ bitmap . data ( ) . data . length === bitmap . dataLength ( ) ;
2009+ }
2010+
2011+ function validateSong ( song : Song ) {
2012+ return song . song &&
2013+ song . song . ticksPerBeat > 0 &&
2014+ song . song . beatsPerMeasure > 0 &&
2015+ song . song . measures > 0 &&
2016+ song . song . beatsPerMinute > 0 &&
2017+ ! ! song . song . tracks ;
2018+ }
2019+
19512020 function getShortIDCore ( assetType : pxt . AssetType , id : string , allowNoPrefix = false ) {
19522021 let prefix : string ;
19532022 switch ( assetType ) {
@@ -2062,12 +2131,6 @@ namespace pxt {
20622131 }
20632132 }
20642133
2065-
2066- function set16Bit ( buf : Uint8ClampedArray , offset : number , value : number ) {
2067- buf [ offset ] = value & 0xff ;
2068- buf [ offset + 1 ] = ( value >> 8 ) & 0xff ;
2069- }
2070-
20712134 function read16Bit ( buf : Uint8ClampedArray , offset : number ) {
20722135 return buf [ offset ] | ( buf [ offset + 1 ] << 8 )
20732136 }
0 commit comments