Spaces:
Restarting
Restarting
File size: 7,096 Bytes
30c32c8 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
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.warn('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);
}
let soundFile = zip.file(fileName);
if (!soundFile) {
// look for assetfile in a flat list of files, or in a folder
const fileMatch = new RegExp(`^([^/]*/)?${fileName}$`);
soundFile = zip.file(fileMatch)[0]; // use first matching file
}
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);
}
let dataFormat = storage.DataFormat.WAV;
switch (sound.dataFormat.toLowerCase()) {
case "mp3":
dataFormat = storage.DataFormat.MP3;
break;
case "ogg":
dataFormat = storage.DataFormat.OGG;
break;
case "flac":
dataFormat = storage.DataFormat.FLAC;
break;
}
return soundFile.async('uint8array').then(data => storage.createAsset(
storage.AssetType.Sound,
dataFormat,
data,
null,
true
))
.then(asset => {
sound.asset = asset;
sound.assetId = asset.assetId;
sound.md5 = `${asset.assetId}.${asset.dataFormat}`;
});
};
/**
* 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])
* @param {string} textLayerFileName Optional file name for the given asset's text layer
* (sb2 only; files have filenames of the form [int].png)
* @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, textLayerFileName) {
const storage = runtime.storage;
const assetId = costume.assetId;
const fileName = assetFileName ? assetFileName :
`${assetId}.${costume.dataFormat}`;
if (!storage) {
log.warn('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])),
null,
true
)).then(asset => {
costume.asset = asset;
costume.assetId = asset.assetId;
costume.md5 = `${asset.assetId}.${asset.dataFormat}`;
});
}
if (!zip) {
// Zip will not be provided if loading project json from server
return Promise.resolve(null);
}
let costumeFile = zip.file(fileName);
if (!costumeFile) {
// look for assetfile in a flat list of files, or in a folder
const fileMatch = new RegExp(`^([^/]*/)?${fileName}$`);
costumeFile = zip.file(fileMatch)[0]; // use the first matched file
}
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);
}
// textLayerMD5 exists if there is a text layer, which is a png of text from Scratch 1.4
// that was opened in Scratch 2.0. In this case, set costume.textLayerAsset.
let textLayerFilePromise;
if (costume.textLayerMD5) {
const textLayerFile = zip.file(textLayerFileName);
if (!textLayerFile) {
log.error(`Could not find text layer file associated with the ${costume.name} costume.`);
return Promise.resolve(null);
}
textLayerFilePromise = textLayerFile.async('uint8array')
.then(data => storage.createAsset(
storage.AssetType.ImageBitmap,
'png',
data,
costume.textLayerMD5
))
.then(asset => {
costume.textLayerAsset = asset;
});
} else {
textLayerFilePromise = Promise.resolve(null);
}
return Promise.all([textLayerFilePromise,
costumeFile.async('uint8array')
.then(data => storage.createAsset(
assetType,
// TODO eventually we want to map non-png's to their actual file types?
costumeFormat,
data,
null,
true
))
.then(asset => {
costume.asset = asset;
costume.assetId = asset.assetId;
costume.md5 = `${asset.assetId}.${asset.dataFormat}`;
})
]);
};
module.exports = {
deserializeSound,
deserializeCostume
};
|