diff --git a/lib/sb3.js b/lib/sb3.js index 811bfd5..2002ff2 100644 --- a/lib/sb3.js +++ b/lib/sb3.js @@ -52,7 +52,15 @@ const extract = function (targets, attribute, id, hash) { const asset = targets[t][attribute][a]; occurrences++; if (typeof id !== 'undefined') idList.push(asset[id]); - if (typeof hash !== 'undefined') hashList.push(asset[hash]); + if (typeof hash !== 'undefined') { + if (asset[hash]) { + hashList.push(asset[hash]); + } else if (hash === 'md5ext') { + // Some projects don't have an md5ext field for some reason, + // extract that data from the assetId and dataFormat + hashList.push(asset.assetId + '.' + asset.dataFormat); + } + } } } diff --git a/test/fixtures/699953650.json b/test/fixtures/699953650.json new file mode 100644 index 0000000..4a4a86d --- /dev/null +++ b/test/fixtures/699953650.json @@ -0,0 +1 @@ +{"targets":[{"isStage":true,"name":"Stage","variables":{"`jEk@4|i[#Fk?(8x)AV.-my variable":["my variable",0]},"lists":{},"broadcasts":{},"blocks":{},"comments":{},"currentCostume":4,"costumes":[{"name":"backdrop1","dataFormat":"svg","assetId":"cd21514d0531fdffb22204e0ec5ed84a","md5ext":"cd21514d0531fdffb22204e0ec5ed84a.svg","rotationCenterX":240,"rotationCenterY":180},{"name":"Blue Sky","bitmapResolution":1,"dataFormat":"svg","assetId":"e7c147730f19d284bcd7b3f00af19bb6","rotationCenterX":240,"rotationCenterY":180},{"name":"Refrigerator","bitmapResolution":1,"dataFormat":"svg","assetId":"98f053f9681e872f34fafd783ce72205","rotationCenterX":240,"rotationCenterY":180},{"name":"Neon Tunnel","bitmapResolution":2,"dataFormat":"png","assetId":"57d2b13b2f73d3d878c72810c137b0d6","rotationCenterX":480,"rotationCenterY":360},{"name":"Nebula","bitmapResolution":2,"dataFormat":"png","assetId":"9b5cdbd596da1b6149f56b794b6394f4","rotationCenterX":480,"rotationCenterY":360}],"sounds":[{"name":"pop","assetId":"83a9787d4cb6f3b7632b4ddfebf74367","dataFormat":"wav","format":"","rate":48000,"sampleCount":1123,"md5ext":"83a9787d4cb6f3b7632b4ddfebf74367.wav"}],"volume":100,"layerOrder":0,"tempo":60,"videoTransparency":50,"videoState":"on","textToSpeechLanguage":null},{"isStage":false,"name":"Champ99","variables":{},"lists":{},"broadcasts":{},"blocks":{"m+g,ch+@:#uIX6HQasdr":{"opcode":"event_whenflagclicked","next":"vuD1}/+AoTGn^QY9$N5u","parent":null,"inputs":{},"fields":{},"shadow":false,"topLevel":true,"x":237,"y":201},"vuD1}/+AoTGn^QY9$N5u":{"opcode":"sensing_askandwait","next":null,"parent":"m+g,ch+@:#uIX6HQasdr","inputs":{"QUESTION":[1,[10,"What's your name?"]]},"fields":{},"shadow":false,"topLevel":false}},"comments":{},"currentCostume":0,"costumes":[{"name":"champ99-a","bitmapResolution":2,"dataFormat":"png","assetId":"7b073f47fbd9421e0d60daacc157f506","md5ext":"7b073f47fbd9421e0d60daacc157f506.png","rotationCenterX":248,"rotationCenterY":306},{"name":"champ99-b","bitmapResolution":2,"dataFormat":"png","assetId":"d6ae13605610aa008d48b0c8b25a57d3","md5ext":"d6ae13605610aa008d48b0c8b25a57d3.png","rotationCenterX":164,"rotationCenterY":290},{"name":"champ99-c","bitmapResolution":2,"dataFormat":"png","assetId":"26fdff424232926001d20041c3d5673b","md5ext":"26fdff424232926001d20041c3d5673b.png","rotationCenterX":152,"rotationCenterY":270},{"name":"champ99-d","bitmapResolution":2,"dataFormat":"png","assetId":"a28ffc2b129fb359ff22c79c48341267","md5ext":"a28ffc2b129fb359ff22c79c48341267.png","rotationCenterX":188,"rotationCenterY":260},{"name":"champ99-e","bitmapResolution":2,"dataFormat":"png","assetId":"56f3220fa82d99dcfc7d27d433ed01e4","md5ext":"56f3220fa82d99dcfc7d27d433ed01e4.png","rotationCenterX":190,"rotationCenterY":248},{"name":"champ99-f","bitmapResolution":2,"dataFormat":"png","assetId":"68453506ae4b6b60a3fc6817ba39d492","md5ext":"68453506ae4b6b60a3fc6817ba39d492.png","rotationCenterX":114,"rotationCenterY":250},{"name":"champ99-g","bitmapResolution":2,"dataFormat":"png","assetId":"20318b14a332fd618ec91e7c1de8be9a","md5ext":"20318b14a332fd618ec91e7c1de8be9a.png","rotationCenterX":132,"rotationCenterY":258}],"sounds":[{"name":"dance celebrate","assetId":"0edb8fb88af19e6e17d0f8cf64c1d136","dataFormat":"wav","format":"adpcm","rate":22050,"sampleCount":176785,"md5ext":"0edb8fb88af19e6e17d0f8cf64c1d136.wav"}],"volume":100,"layerOrder":1,"visible":true,"x":21,"y":-50,"size":100,"direction":90,"draggable":false,"rotationStyle":"all around"}],"monitors":[],"extensions":[],"meta":{"semver":"3.0.0","vm":"0.2.0-prerelease.20220525223828","agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36 Edg/101.0.1210.53"}} \ No newline at end of file diff --git a/test/unit/missing_md5ext.js b/test/unit/missing_md5ext.js new file mode 100644 index 0000000..13132af --- /dev/null +++ b/test/unit/missing_md5ext.js @@ -0,0 +1,34 @@ +const fs = require('fs'); +const path = require('path'); +const test = require('tap').test; +const analysis = require('../../lib/index'); + +// Test project with missing 'md5ext' field +const defaultObject = fs.readFileSync( + path.resolve(__dirname, '../fixtures/699953650.json') +); + +test('Project with missing "md5ext" property can be analyzed correctly', t => { + analysis(defaultObject, (err, result) => { + t.not(err); + t.ok(result); + + // Test that all the costume and sound hashes are not falsey + t.equal(result.costumes.count, result.costumes.hash.length); + t.equal(result.sounds.count, result.sounds.hash.length); + + for (let i = 0; i < result.costumes.count; i++) { + const costumeHash = result.costumes.hash[i]; + t.type(costumeHash, 'string'); + t.ok(costumeHash); + } + + for (let i = 0; i < result.sounds.count; i++) { + const soundHash = result.sounds.hash[i]; + t.type(soundHash, 'string'); + t.ok(soundHash); + } + + t.end(); + }); +});