Skip to content

Commit

Permalink
Allow serializing and deserializing infinite length comments
Browse files Browse the repository at this point in the history
  • Loading branch information
GarboMuffin committed Dec 31, 2023
1 parent c309c42 commit 7e9dab8
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 2 deletions.
17 changes: 15 additions & 2 deletions src/serialization/sb3.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ const primitiveOpcodeInfoMap = {
data_listcontents: [LIST_PRIMITIVE, 'LIST']
};

// We don't enforce this limit, but Scratch does, so we need to handle it for compatibility.
const UPSTREAM_MAX_COMMENT_LENGTH = 8000;

/**
* Serializes primitives described above into a more compact format
* @param {object} block the block to serialize
Expand Down Expand Up @@ -555,7 +558,16 @@ const serializeComments = function (comments) {
serializedComment.width = comment.width;
serializedComment.height = comment.height;
serializedComment.minimized = comment.minimized;
serializedComment.text = comment.text;

if (comment.text.length > UPSTREAM_MAX_COMMENT_LENGTH) {
// Upstream's scratch-parser will refuse to load projects if the text is too long, so to maximize
// compatibility and minimize redundancy we'll store a truncated version in .text and the rest in
// another field
serializedComment.text = comment.text.substring(0, UPSTREAM_MAX_COMMENT_LENGTH);
serializedComment.extraText = comment.text.substring(UPSTREAM_MAX_COMMENT_LENGTH);
} else {
serializedComment.text = comment.text;
}

obj[commentId] = serializedComment;
}
Expand Down Expand Up @@ -1213,7 +1225,8 @@ const parseScratchObject = function (object, runtime, extensions, zip, assets) {
const comment = object.comments[commentId];
const newComment = new Comment(
commentId,
comment.text,
// text has a length limit, so anything extra got saved in extraText
comment.text + (typeof comment.extraText === 'string' ? comment.extraText : ''),
comment.x,
comment.y,
comment.width,
Expand Down
Binary file added test/fixtures/tw-very-long-comments.sb3
Binary file not shown.
82 changes: 82 additions & 0 deletions test/integration/tw_very_long_comments.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
const {test} = require('tap');
const fs = require('fs');
const VirtualMachine = require('../../src/virtual-machine');
const Runtime = require('../../src/engine/runtime');
const sb3 = require('../../src/serialization/sb3');
const RenderedTarget = require('../../src/sprites/rendered-target');
const Sprite = require('../../src/sprites/sprite');
const path = require('path');

test('serializes very long comments', t => {
const rt = new Runtime();
const sprite = new Sprite();
const target = new RenderedTarget(sprite);
rt.addTarget(target);

target.createComment('id_short', null, 'short comment', 0, 0, 20, 20, false);
target.createComment('id_max_length', null, `start${'0'.repeat(8000 - 5)}`, 0, 0, 20, 20, false);
target.createComment('id_max_length_plus_1', null, `start${'0'.repeat(8000 - 5)}a`, 0, 0, 20, 20, false);
target.createComment(
'id_way_too_long',
null,
`start${'0'.repeat(8000 - 5 - 3)}endthis should be the truncated part${'0'.repeat(25000)}`,
0,
0,
20,
20,
false
);

const serialized = sb3.serialize(rt, target.id);
const common = {
// same for every block, already tested elsewhere, not interesting for here
blockId: null,
x: 0,
y: 0,
width: 20,
height: 20,
minimized: false
};
t.same(serialized.comments, {
id_short: {
...common,
text: 'short comment'
},
id_max_length: {
...common,
text: `start${'0'.repeat(8000 - 5)}`
},
id_max_length_plus_1: {
...common,
text: `start${'0'.repeat(8000 - 5)}`,
extraText: 'a'
},
id_way_too_long: {
...common,
text: `start${'0'.repeat(8000 - 5 - 3)}end`,
extraText: `this should be the truncated part${'0'.repeat(25000)}`
}
});

t.end();
});

test('deserializes very long comments', t => {
const vm = new VirtualMachine();
const fixture = fs.readFileSync(path.resolve(__dirname, '../fixtures/tw-very-long-comments.sb3'));
vm.loadProject(fixture).then(() => {
const comments = vm.runtime.targets[0].comments;

// note that comment IDs may change each time the test project is saved
t.equal(comments.b.text, '');
t.equal(comments.a.text, 'short');
t.equal(comments.c.text, `exactly length limit${'0'.repeat(8000 - 'exactly length limit'.length)}`);
t.equal(comments.d.text, `length limit + 1${':'.repeat(8000 - 'length limit + 1'.length)}1`);
t.equal(
comments.e.text,
`unreasonably long${'!'.repeat(8000 - 'unreasonably long'.length)}${'123456789'.repeat(2000)}`
);

t.end();
});
});

0 comments on commit 7e9dab8

Please sign in to comment.