scratch-vm/src/serialization/deserialize-assets.js

128 lines
4.7 KiB
JavaScript
Raw Normal View History

const JSZip = require('jszip');
const log = require('../util/log');
/**
* Deserializes sound from file into storage cache so that it can
* be loaded into the runtime.
* @param {object} sound Descriptor for sound from sb3 file
* @param {Runtime} runtime The runtime containing the storage to cache the sounds in
* @param {JSZip} zip The zip containing the sound file being described by `sound`
* @param {string} assetFileName Optional file name for the given asset
* (sb2 files have filenames of the form [int].[ext],
* sb3 files have filenames of the form [md5].[ext])
* @return {Promise} Promise that resolves after the described sound has been stored
* into the runtime storage cache, the sound was already stored, or an error has
* occurred.
*/
const deserializeSound = function (sound, runtime, zip, assetFileName) {
const fileName = assetFileName ? assetFileName : sound.md5;
const storage = runtime.storage;
if (!storage) {
log.error('No storage module present; cannot load sound asset: ', fileName);
return Promise.resolve(null);
}
if (!zip) { // Zip will not be provided if loading project json from server
return Promise.resolve(null);
}
const soundFile = zip.file(fileName);
if (!soundFile) {
log.error(`Could not find sound file associated with the ${sound.name} sound.`);
return Promise.resolve(null);
}
if (!JSZip.support.uint8array) {
log.error('JSZip uint8array is not supported in this browser.');
return Promise.resolve(null);
}
const dataFormat = sound.dataFormat.toLowerCase() === 'mp3' ?
storage.DataFormat.MP3 : storage.DataFormat.WAV;
return soundFile.async('uint8array').then(data => storage.createAsset(
storage.AssetType.Sound,
dataFormat,
data,
sound.assetId
));
};
/**
* Deserializes costume from file into storage cache so that it can
* be loaded into the runtime.
* @param {object} costume Descriptor for costume from sb3 file
* @param {Runtime} runtime The runtime containing the storage to cache the costumes in
* @param {JSZip} zip The zip containing the costume file being described by `costume`
* @param {string} assetFileName Optional file name for the given asset
* (sb2 files have filenames of the form [int].[ext],
* sb3 files have filenames of the form [md5].[ext])
* @return {Promise} Promise that resolves after the described costume has been stored
* into the runtime storage cache, the costume was already stored, or an error has
* occurred.
*/
const deserializeCostume = function (costume, runtime, zip, assetFileName) {
const storage = runtime.storage;
const assetId = costume.assetId;
const fileName = assetFileName ? assetFileName :
`${assetId}.${costume.dataFormat}`;
if (!storage) {
log.error('No storage module present; cannot load costume asset: ', fileName);
return Promise.resolve(null);
}
if (costume.asset) {
// When uploading a sprite from an image file, the asset data will be provided
// @todo Cache the asset data somewhere and pull it out here
return Promise.resolve(storage.createAsset(
costume.asset.assetType,
costume.asset.dataFormat,
new Uint8Array(Object.keys(costume.asset.data).map(key => costume.asset.data[key])),
costume.asset.assetId
))
.then(asset => {
costume.asset = asset;
});
}
if (!zip) {
// Zip will not be provided if loading project json from server
return Promise.resolve(null);
}
const costumeFile = zip.file(fileName);
if (!costumeFile) {
log.error(`Could not find costume file associated with the ${costume.name} costume.`);
return Promise.resolve(null);
}
let assetType = null;
const costumeFormat = costume.dataFormat.toLowerCase();
if (costumeFormat === 'svg') {
assetType = storage.AssetType.ImageVector;
} else if (['png', 'bmp', 'jpeg', 'jpg', 'gif'].indexOf(costumeFormat) >= 0) {
assetType = storage.AssetType.ImageBitmap;
} else {
log.error(`Unexpected file format for costume: ${costumeFormat}`);
}
if (!JSZip.support.uint8array) {
log.error('JSZip uint8array is not supported in this browser.');
return Promise.resolve(null);
}
return costumeFile.async('uint8array').then(data => storage.createAsset(
assetType,
// TODO eventually we want to map non-png's to their actual file types?
costumeFormat,
data,
assetId
))
.then(asset => {
costume.asset = asset;
});
};
module.exports = {
deserializeSound,
deserializeCostume
};