@@ -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 ) ;
@@ -1709,6 +1715,10 @@ namespace pxt {
17091715
17101716 export function cloneAsset < U extends Asset > ( asset : U ) : U {
17111717 asset . meta = Object . assign ( { } , asset . meta ) ;
1718+ if ( asset . meta . temporaryInfo ) {
1719+ asset . meta . temporaryInfo = Object . assign ( { } , asset . meta . temporaryInfo ) ;
1720+ }
1721+
17121722 switch ( asset . type ) {
17131723 case AssetType . Tile :
17141724 case AssetType . Image :
@@ -1948,6 +1958,69 @@ namespace pxt {
19481958 return getShortIDCore ( asset . type , asset . id ) ;
19491959 }
19501960
1961+ export function validateAsset ( asset : pxt . Asset ) {
1962+ if ( ! asset ) return false ;
1963+
1964+ switch ( asset . type ) {
1965+ case AssetType . Image :
1966+ case AssetType . Tile :
1967+ return validateImageAsset ( asset as ProjectImage | Tile ) ;
1968+ case AssetType . Tilemap :
1969+ return validateTilemap ( asset as ProjectTilemap ) ;
1970+ case AssetType . Animation :
1971+ return validateAnimation ( asset as Animation )
1972+ case AssetType . Song :
1973+ return validateSong ( asset as Song ) ;
1974+ }
1975+ }
1976+
1977+ function validateImageAsset ( asset : ProjectImage | Tile ) {
1978+ if ( ! asset . bitmap ) return false ;
1979+
1980+ return validateBitmap ( sprite . Bitmap . fromData ( asset . bitmap ) ) ;
1981+ }
1982+
1983+ function validateTilemap ( tilemap : ProjectTilemap ) {
1984+ if (
1985+ ! tilemap . data ||
1986+ ! tilemap . data . tilemap ||
1987+ ! tilemap . data . tileset ||
1988+ ! tilemap . data . tileset . tileWidth ||
1989+ ! tilemap . data . tileset . tiles ?. length ||
1990+ ! tilemap . data . layers
1991+ ) {
1992+ return false ;
1993+ }
1994+
1995+ return validateBitmap ( sprite . Bitmap . fromData ( tilemap . data . layers ) ) &&
1996+ validateBitmap ( tilemap . data . tilemap ) ;
1997+ }
1998+
1999+ function validateAnimation ( animation : Animation ) {
2000+ if ( ! animation . frames ?. length || animation . interval <= 0 ) {
2001+ return false ;
2002+ }
2003+
2004+ return ! animation . frames . some ( frame => ! validateBitmap ( sprite . Bitmap . fromData ( frame ) ) ) ;
2005+ }
2006+
2007+ function validateBitmap ( bitmap : sprite . Bitmap ) {
2008+ return bitmap . width > 0 &&
2009+ bitmap . height > 0 &&
2010+ ! Number . isNaN ( bitmap . x0 ) &&
2011+ ! Number . isNaN ( bitmap . y0 ) &&
2012+ bitmap . data ( ) . data . length === bitmap . dataLength ( ) ;
2013+ }
2014+
2015+ function validateSong ( song : Song ) {
2016+ return song . song &&
2017+ song . song . ticksPerBeat > 0 &&
2018+ song . song . beatsPerMeasure > 0 &&
2019+ song . song . measures > 0 &&
2020+ song . song . beatsPerMinute > 0 &&
2021+ ! ! song . song . tracks ;
2022+ }
2023+
19512024 function getShortIDCore ( assetType : pxt . AssetType , id : string , allowNoPrefix = false ) {
19522025 let prefix : string ;
19532026 switch ( assetType ) {
@@ -2062,12 +2135,6 @@ namespace pxt {
20622135 }
20632136 }
20642137
2065-
2066- function set16Bit ( buf : Uint8ClampedArray , offset : number , value : number ) {
2067- buf [ offset ] = value & 0xff ;
2068- buf [ offset + 1 ] = ( value >> 8 ) & 0xff ;
2069- }
2070-
20712138 function read16Bit ( buf : Uint8ClampedArray , offset : number ) {
20722139 return buf [ offset ] | ( buf [ offset + 1 ] << 8 )
20732140 }
@@ -2087,4 +2154,21 @@ namespace pxt {
20872154 case AssetType . Song : return snapshot . songs ;
20882155 }
20892156 }
2157+
2158+ export function patchTemporaryAsset ( oldValue : pxt . Asset , newValue : pxt . Asset , project : TilemapProject ) {
2159+ if ( ! oldValue || assetEquals ( oldValue , newValue ) ) return newValue ;
2160+
2161+ newValue = cloneAsset ( newValue ) ;
2162+ const wasTemporary = ! oldValue . meta . displayName ;
2163+ const isTemporary = ! newValue . meta . displayName ;
2164+
2165+ // if we went from being temporary to no longer being temporary,
2166+ // make sure we replace the junk id with a new value
2167+ if ( wasTemporary && ! isTemporary ) {
2168+ newValue . id = project . generateNewID ( newValue . type ) ;
2169+ newValue . internalID = project . getNewInternalId ( ) ;
2170+ }
2171+
2172+ return newValue ;
2173+ }
20902174}
0 commit comments