@coderline/alphatab 1.9.0-alpha.1803 → 1.9.0-alpha.1806
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/alphaTab.core.min.mjs +2 -2
- package/dist/alphaTab.core.mjs +188 -72
- package/dist/alphaTab.d.ts +40 -0
- package/dist/alphaTab.js +188 -72
- package/dist/alphaTab.min.js +2 -2
- package/dist/alphaTab.min.mjs +1 -1
- package/dist/alphaTab.mjs +1 -1
- package/dist/alphaTab.worker.min.mjs +1 -1
- package/dist/alphaTab.worker.mjs +1 -1
- package/dist/alphaTab.worklet.min.mjs +1 -1
- package/dist/alphaTab.worklet.mjs +1 -1
- package/package.json +1 -1
package/dist/alphaTab.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* alphaTab v1.9.0-alpha.
|
|
2
|
+
* alphaTab v1.9.0-alpha.1806 (develop, build 1806)
|
|
3
3
|
*
|
|
4
4
|
* Copyright © 2026, Daniel Kuschny and Contributors, All rights reserved.
|
|
5
5
|
*
|
|
@@ -209,9 +209,9 @@
|
|
|
209
209
|
* @internal
|
|
210
210
|
*/
|
|
211
211
|
class VersionInfo {
|
|
212
|
-
static version = '1.9.0-alpha.
|
|
213
|
-
static date = '2026-05-
|
|
214
|
-
static commit = '
|
|
212
|
+
static version = '1.9.0-alpha.1806';
|
|
213
|
+
static date = '2026-05-18T04:36:35.809Z';
|
|
214
|
+
static commit = 'e926f2b6bcfbaa219a04140bf34fdd9431a6ca17';
|
|
215
215
|
static print(print) {
|
|
216
216
|
print(`alphaTab ${VersionInfo.version}`);
|
|
217
217
|
print(`commit: ${VersionInfo.commit}`);
|
|
@@ -10321,7 +10321,7 @@
|
|
|
10321
10321
|
encoding = 'utf-8';
|
|
10322
10322
|
}
|
|
10323
10323
|
const decoder = new TextDecoder(encoding);
|
|
10324
|
-
return decoder.decode(data
|
|
10324
|
+
return decoder.decode(data);
|
|
10325
10325
|
}
|
|
10326
10326
|
static _detectEncoding(data) {
|
|
10327
10327
|
if (data.length > 2 && data[0] === 0xfe && data[1] === 0xff) {
|
|
@@ -16007,6 +16007,68 @@
|
|
|
16007
16007
|
}
|
|
16008
16008
|
}
|
|
16009
16009
|
|
|
16010
|
+
/**
|
|
16011
|
+
* Thrown whenever we hit the end of input data unexpectedly.
|
|
16012
|
+
* @public
|
|
16013
|
+
*/
|
|
16014
|
+
class EndOfReaderError extends AlphaTabError {
|
|
16015
|
+
constructor() {
|
|
16016
|
+
super(exports.AlphaTabErrorType.Format, 'Unexpected end of data within reader');
|
|
16017
|
+
}
|
|
16018
|
+
}
|
|
16019
|
+
/**
|
|
16020
|
+
* Thrown whenever an overflow in data or buffer sizes is detected.
|
|
16021
|
+
* @public
|
|
16022
|
+
*/
|
|
16023
|
+
class OverflowError extends AlphaTabError {
|
|
16024
|
+
constructor(message) {
|
|
16025
|
+
super(exports.AlphaTabErrorType.Format, message);
|
|
16026
|
+
}
|
|
16027
|
+
}
|
|
16028
|
+
/**
|
|
16029
|
+
* An {@see IReadable} implementation throwing when the end of stream is reached guarding against
|
|
16030
|
+
* corrupted or maliciously crafted files leading to endless reading
|
|
16031
|
+
* @internal
|
|
16032
|
+
*/
|
|
16033
|
+
class ThrowingReadable {
|
|
16034
|
+
_readable;
|
|
16035
|
+
constructor(readable) {
|
|
16036
|
+
this._readable = readable;
|
|
16037
|
+
}
|
|
16038
|
+
get position() {
|
|
16039
|
+
return this._readable.position;
|
|
16040
|
+
}
|
|
16041
|
+
set position(value) {
|
|
16042
|
+
this._readable.position = value;
|
|
16043
|
+
}
|
|
16044
|
+
get length() {
|
|
16045
|
+
return this._readable.length;
|
|
16046
|
+
}
|
|
16047
|
+
reset() {
|
|
16048
|
+
this._readable.reset();
|
|
16049
|
+
}
|
|
16050
|
+
skip(offset) {
|
|
16051
|
+
this._readable.skip(offset);
|
|
16052
|
+
}
|
|
16053
|
+
_requireBytes(bytes) {
|
|
16054
|
+
const remaining = this.length - this.position;
|
|
16055
|
+
if (remaining < bytes) {
|
|
16056
|
+
throw new EndOfReaderError();
|
|
16057
|
+
}
|
|
16058
|
+
}
|
|
16059
|
+
readByte() {
|
|
16060
|
+
this._requireBytes(1);
|
|
16061
|
+
return this._readable.readByte();
|
|
16062
|
+
}
|
|
16063
|
+
read(buffer, offset, count) {
|
|
16064
|
+
this._requireBytes(count);
|
|
16065
|
+
return this._readable.read(buffer, offset, count);
|
|
16066
|
+
}
|
|
16067
|
+
readAll() {
|
|
16068
|
+
return this._readable.readAll();
|
|
16069
|
+
}
|
|
16070
|
+
}
|
|
16071
|
+
|
|
16010
16072
|
/**
|
|
16011
16073
|
* This is the base public class for creating new song importers which
|
|
16012
16074
|
* enable reading scores from any binary datasource
|
|
@@ -16019,7 +16081,12 @@
|
|
|
16019
16081
|
* Initializes the importer with the given data and settings.
|
|
16020
16082
|
*/
|
|
16021
16083
|
init(data, settings) {
|
|
16022
|
-
|
|
16084
|
+
if (data instanceof ThrowingReadable) {
|
|
16085
|
+
this.data = data;
|
|
16086
|
+
}
|
|
16087
|
+
else {
|
|
16088
|
+
this.data = new ThrowingReadable(data);
|
|
16089
|
+
}
|
|
16023
16090
|
this.settings = settings;
|
|
16024
16091
|
// when beginning reading a new score we reset the IDs.
|
|
16025
16092
|
Score.resetIds();
|
|
@@ -17688,8 +17755,10 @@
|
|
|
17688
17755
|
*/
|
|
17689
17756
|
class ZipReader {
|
|
17690
17757
|
_readable;
|
|
17691
|
-
|
|
17758
|
+
_maxDecodingBufferSize;
|
|
17759
|
+
constructor(readable, maxDecodingBufferSize) {
|
|
17692
17760
|
this._readable = readable;
|
|
17761
|
+
this._maxDecodingBufferSize = maxDecodingBufferSize;
|
|
17693
17762
|
}
|
|
17694
17763
|
read() {
|
|
17695
17764
|
const entries = [];
|
|
@@ -17721,7 +17790,13 @@
|
|
|
17721
17790
|
IOHelper.readInt32LE(readable); // crc-32
|
|
17722
17791
|
IOHelper.readInt32LE(readable); // compressed size
|
|
17723
17792
|
const uncompressedSize = IOHelper.readInt32LE(readable);
|
|
17793
|
+
if (uncompressedSize > this._maxDecodingBufferSize) {
|
|
17794
|
+
throw new OverflowError(`Zip contains files exceeding the configured maxDecodingBufferSize`);
|
|
17795
|
+
}
|
|
17724
17796
|
const fileNameLength = IOHelper.readInt16LE(readable);
|
|
17797
|
+
if (fileNameLength > this._maxDecodingBufferSize) {
|
|
17798
|
+
throw new OverflowError(`Zip contains file names exceeding the configured maxDecodingBufferSize`);
|
|
17799
|
+
}
|
|
17725
17800
|
const extraFieldLength = IOHelper.readInt16LE(readable);
|
|
17726
17801
|
const fname = IOHelper.toString(IOHelper.readByteArray(readable, fileNameLength), 'utf-8');
|
|
17727
17802
|
readable.skip(extraFieldLength);
|
|
@@ -17734,6 +17809,9 @@
|
|
|
17734
17809
|
while (true) {
|
|
17735
17810
|
const bytes = z.readBytes(buffer, 0, buffer.length);
|
|
17736
17811
|
target.write(buffer, 0, bytes);
|
|
17812
|
+
if (target.length > this._maxDecodingBufferSize) {
|
|
17813
|
+
throw new OverflowError(`Zip entry "${fname}" contains data exceeding the configured maxDecodingBufferSize`);
|
|
17814
|
+
}
|
|
17737
17815
|
if (bytes < buffer.length) {
|
|
17738
17816
|
break;
|
|
17739
17817
|
}
|
|
@@ -19682,7 +19760,7 @@
|
|
|
19682
19760
|
}
|
|
19683
19761
|
readScore() {
|
|
19684
19762
|
Logger.debug(this.name, 'Loading ZIP entries');
|
|
19685
|
-
const fileSystem = new ZipReader(this.data);
|
|
19763
|
+
const fileSystem = new ZipReader(this.data, this.settings.importer.maxDecodingBufferSize);
|
|
19686
19764
|
let entries;
|
|
19687
19765
|
let xml = null;
|
|
19688
19766
|
entries = fileSystem.read();
|
|
@@ -19751,7 +19829,7 @@
|
|
|
19751
19829
|
static _versionString = 'FICHIER GUITAR PRO ';
|
|
19752
19830
|
// NOTE: General Midi only defines percussion instruments from 35-81
|
|
19753
19831
|
// Guitar Pro 5 allowed GS extensions (27-34 and 82-87)
|
|
19754
|
-
// GP7-8 do not have all these definitions anymore, this lookup ensures some fallback
|
|
19832
|
+
// GP7-8 do not have all these definitions anymore, this lookup ensures some fallback
|
|
19755
19833
|
// (even if they are not correct)
|
|
19756
19834
|
// we can support this properly in future when we allow custom alphaTex articulation definitions
|
|
19757
19835
|
// then we don't need to rely on GP specifics anymore but handle things on export/import
|
|
@@ -19812,7 +19890,7 @@
|
|
|
19812
19890
|
this._initialTempo = Automation.buildTempoAutomation(false, 0, 0, 0);
|
|
19813
19891
|
if (this._versionNumber >= 500) {
|
|
19814
19892
|
this.readPageSetup();
|
|
19815
|
-
this._initialTempo.text = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding);
|
|
19893
|
+
this._initialTempo.text = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
19816
19894
|
}
|
|
19817
19895
|
// tempo stuff
|
|
19818
19896
|
this._initialTempo.value = IOHelper.readInt32LE(this.data);
|
|
@@ -19851,7 +19929,9 @@
|
|
|
19851
19929
|
}
|
|
19852
19930
|
// contents
|
|
19853
19931
|
this._barCount = IOHelper.readInt32LE(this.data);
|
|
19932
|
+
this._ensureLoopBoundary(this._barCount, Gp3To5Importer._maxBarCount, 'bar count');
|
|
19854
19933
|
this._trackCount = IOHelper.readInt32LE(this.data);
|
|
19934
|
+
this._ensureLoopBoundary(this._trackCount, Gp3To5Importer._maxTrackCount, 'track count');
|
|
19855
19935
|
this.readMasterBars();
|
|
19856
19936
|
this.readTracks();
|
|
19857
19937
|
this.readBars();
|
|
@@ -19899,35 +19979,54 @@
|
|
|
19899
19979
|
Logger.debug(this.name, `Guitar Pro version ${version} detected`);
|
|
19900
19980
|
}
|
|
19901
19981
|
readScoreInformation() {
|
|
19902
|
-
this._score.title = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding);
|
|
19903
|
-
this._score.subTitle = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding);
|
|
19904
|
-
this._score.artist = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding);
|
|
19905
|
-
this._score.album = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding);
|
|
19906
|
-
this._score.words = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding);
|
|
19982
|
+
this._score.title = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
19983
|
+
this._score.subTitle = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
19984
|
+
this._score.artist = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
19985
|
+
this._score.album = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
19986
|
+
this._score.words = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
19907
19987
|
this._score.music =
|
|
19908
19988
|
this._versionNumber >= 500
|
|
19909
|
-
? GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding)
|
|
19989
|
+
? GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize)
|
|
19910
19990
|
: this._score.words;
|
|
19911
|
-
this._score.copyright = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding);
|
|
19912
|
-
this._score.tab = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding);
|
|
19913
|
-
this._score.instructions = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding);
|
|
19991
|
+
this._score.copyright = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
19992
|
+
this._score.tab = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
19993
|
+
this._score.instructions = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
19914
19994
|
const noticeLines = IOHelper.readInt32LE(this.data);
|
|
19995
|
+
this._ensureLoopBoundary(noticeLines, Gp3To5Importer._maxNoticeLines, 'notice line count');
|
|
19915
19996
|
let notice = '';
|
|
19916
19997
|
for (let i = 0; i < noticeLines; i++) {
|
|
19917
19998
|
if (i > 0) {
|
|
19918
19999
|
notice += '\r\n';
|
|
19919
20000
|
}
|
|
19920
|
-
notice += GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding)?.toString();
|
|
20001
|
+
notice += GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize)?.toString();
|
|
19921
20002
|
}
|
|
19922
20003
|
this._score.notices = notice;
|
|
19923
20004
|
}
|
|
20005
|
+
// very generous thresholds for values which control loop boundaries
|
|
20006
|
+
// prevents DoS or resource exhaustion for corrupt files or files with malicious intent
|
|
20007
|
+
// not configurable, as realistically GP3-5 files will not exceed these values,
|
|
20008
|
+
// I don't hink anyone is that verbose in the small GP5 box where you can add notices
|
|
20009
|
+
static _maxNoticeLines = 1000;
|
|
20010
|
+
// I haven't encountered such a long song in the wild. beyond 1000 bars something is clearly off
|
|
20011
|
+
static _maxBarCount = 1000;
|
|
20012
|
+
// I think GP5 itself limits already to ~10. 100 tracks is just unrealistic, proof me wrong
|
|
20013
|
+
static _maxTrackCount = 100;
|
|
20014
|
+
// nobody reallistically writes that many beats in one bar either.
|
|
20015
|
+
static _maxBeatCount = 100;
|
|
20016
|
+
// I think GP5 already limits this to way less, very generous to allow 4 times more than likely the UI supports
|
|
20017
|
+
static _maxBendPointCount = BendPoint.MaxPosition * 4;
|
|
20018
|
+
_ensureLoopBoundary(value, maximumValue, label) {
|
|
20019
|
+
if (value > maximumValue) {
|
|
20020
|
+
throw new OverflowError(`'${label}' with value ${value} has exceeded the internal safety threshold of ${maximumValue}`);
|
|
20021
|
+
}
|
|
20022
|
+
}
|
|
19924
20023
|
readLyrics() {
|
|
19925
20024
|
this._lyrics = [];
|
|
19926
20025
|
this._lyricsTrack = IOHelper.readInt32LE(this.data) - 1;
|
|
19927
20026
|
for (let i = 0; i < 5; i++) {
|
|
19928
20027
|
const lyrics = new Lyrics();
|
|
19929
20028
|
lyrics.startBar = IOHelper.readInt32LE(this.data) - 1;
|
|
19930
|
-
lyrics.text = GpBinaryHelpers.gpReadStringInt(this.data, this.settings.importer.encoding);
|
|
20029
|
+
lyrics.text = GpBinaryHelpers.gpReadStringInt(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
19931
20030
|
this._lyrics.push(lyrics);
|
|
19932
20031
|
}
|
|
19933
20032
|
}
|
|
@@ -19944,41 +20043,41 @@
|
|
|
19944
20043
|
ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Title).isVisible =
|
|
19945
20044
|
(flags & (0x01 << 0)) !== 0;
|
|
19946
20045
|
ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Title).template =
|
|
19947
|
-
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding);
|
|
20046
|
+
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
19948
20047
|
ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.SubTitle).isVisible =
|
|
19949
20048
|
(flags & (0x01 << 1)) !== 0;
|
|
19950
20049
|
ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.SubTitle).template =
|
|
19951
|
-
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding);
|
|
20050
|
+
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
19952
20051
|
ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Artist).isVisible =
|
|
19953
20052
|
(flags & (0x01 << 2)) !== 0;
|
|
19954
20053
|
ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Artist).template =
|
|
19955
|
-
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding);
|
|
20054
|
+
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
19956
20055
|
ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Album).isVisible =
|
|
19957
20056
|
(flags & (0x01 << 3)) !== 0;
|
|
19958
20057
|
ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Album).template =
|
|
19959
|
-
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding);
|
|
20058
|
+
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
19960
20059
|
ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Words).isVisible =
|
|
19961
20060
|
(flags & (0x01 << 4)) !== 0;
|
|
19962
20061
|
ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Words).template =
|
|
19963
|
-
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding);
|
|
20062
|
+
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
19964
20063
|
ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Music).isVisible =
|
|
19965
20064
|
(flags & (0x01 << 5)) !== 0;
|
|
19966
20065
|
ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Music).template =
|
|
19967
|
-
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding);
|
|
20066
|
+
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
19968
20067
|
ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.WordsAndMusic).isVisible =
|
|
19969
20068
|
(flags & (0x01 << 6)) !== 0;
|
|
19970
20069
|
ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.WordsAndMusic).template =
|
|
19971
|
-
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding);
|
|
20070
|
+
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
19972
20071
|
ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Copyright).isVisible =
|
|
19973
20072
|
(flags & (0x01 << 7)) !== 0;
|
|
19974
20073
|
ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.Copyright).template =
|
|
19975
|
-
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding);
|
|
20074
|
+
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
19976
20075
|
ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.CopyrightSecondLine).isVisible =
|
|
19977
20076
|
(flags & (0x01 << 7)) !== 0;
|
|
19978
20077
|
ModelUtils.getOrCreateHeaderFooterStyle(this._score, ScoreSubElement.CopyrightSecondLine).template =
|
|
19979
|
-
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding);
|
|
20078
|
+
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
19980
20079
|
// page number format
|
|
19981
|
-
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding);
|
|
20080
|
+
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
19982
20081
|
}
|
|
19983
20082
|
readPlaybackInfos() {
|
|
19984
20083
|
this._playbackInfos = [];
|
|
@@ -20059,7 +20158,7 @@
|
|
|
20059
20158
|
// marker
|
|
20060
20159
|
if ((flags & 0x20) !== 0) {
|
|
20061
20160
|
const section = new Section();
|
|
20062
|
-
section.text = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding);
|
|
20161
|
+
section.text = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
20063
20162
|
section.marker = '';
|
|
20064
20163
|
GpBinaryHelpers.gpReadColor(this.data, false);
|
|
20065
20164
|
newMasterBar.section = section;
|
|
@@ -20226,9 +20325,9 @@
|
|
|
20226
20325
|
// 1 byte PRE
|
|
20227
20326
|
this.data.skip(4);
|
|
20228
20327
|
// RSE: effect name
|
|
20229
|
-
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding);
|
|
20328
|
+
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
20230
20329
|
// RSE: effect category
|
|
20231
|
-
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding);
|
|
20330
|
+
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
20232
20331
|
}
|
|
20233
20332
|
}
|
|
20234
20333
|
else {
|
|
@@ -20285,6 +20384,7 @@
|
|
|
20285
20384
|
}
|
|
20286
20385
|
const newVoice = new Voice$1();
|
|
20287
20386
|
bar.addVoice(newVoice);
|
|
20387
|
+
this._ensureLoopBoundary(beatCount, Gp3To5Importer._maxBeatCount, 'beat count');
|
|
20288
20388
|
for (let i = 0; i < beatCount; i++) {
|
|
20289
20389
|
this.readBeat(track, bar, newVoice);
|
|
20290
20390
|
}
|
|
@@ -20363,7 +20463,7 @@
|
|
|
20363
20463
|
}
|
|
20364
20464
|
const beatTextAsLyrics = this.settings.importer.beatTextAsLyrics && track.index !== this._lyricsTrack; // detect if not lyrics track
|
|
20365
20465
|
if ((flags & 0x04) !== 0) {
|
|
20366
|
-
const text = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding);
|
|
20466
|
+
const text = GpBinaryHelpers.gpReadStringIntUnused(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
20367
20467
|
if (beatTextAsLyrics) {
|
|
20368
20468
|
const lyrics = new Lyrics();
|
|
20369
20469
|
lyrics.text = text.trim();
|
|
@@ -20538,7 +20638,7 @@
|
|
|
20538
20638
|
}
|
|
20539
20639
|
else {
|
|
20540
20640
|
const strings = this._versionNumber >= 406 ? 7 : 6;
|
|
20541
|
-
chord.name = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding);
|
|
20641
|
+
chord.name = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
20542
20642
|
chord.firstFret = IOHelper.readInt32LE(this.data);
|
|
20543
20643
|
if (chord.firstFret > 0) {
|
|
20544
20644
|
for (let i = 0; i < strings; i++) {
|
|
@@ -20649,6 +20749,7 @@
|
|
|
20649
20749
|
this.data.readByte(); // type
|
|
20650
20750
|
IOHelper.readInt32LE(this.data); // value
|
|
20651
20751
|
const pointCount = IOHelper.readInt32LE(this.data);
|
|
20752
|
+
this._ensureLoopBoundary(pointCount, Gp3To5Importer._maxBendPointCount, 'tremolo bar point count');
|
|
20652
20753
|
if (pointCount > 0) {
|
|
20653
20754
|
for (let i = 0; i < pointCount; i++) {
|
|
20654
20755
|
const point = new BendPoint(0, 0);
|
|
@@ -20708,7 +20809,7 @@
|
|
|
20708
20809
|
const phaser = IOHelper.readSInt8(this.data);
|
|
20709
20810
|
const tremolo = IOHelper.readSInt8(this.data);
|
|
20710
20811
|
if (this._versionNumber >= 500) {
|
|
20711
|
-
tableChange.tempoName = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding);
|
|
20812
|
+
tableChange.tempoName = GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
20712
20813
|
}
|
|
20713
20814
|
tableChange.tempo = IOHelper.readInt32LE(this.data);
|
|
20714
20815
|
// durations (in number of beats)
|
|
@@ -20752,8 +20853,8 @@
|
|
|
20752
20853
|
}
|
|
20753
20854
|
// unknown
|
|
20754
20855
|
if (this._versionNumber >= 510) {
|
|
20755
|
-
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding);
|
|
20756
|
-
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding);
|
|
20856
|
+
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
20857
|
+
GpBinaryHelpers.gpReadStringIntByte(this.data, this.settings.importer.encoding, this.settings.importer.maxDecodingBufferSize);
|
|
20757
20858
|
}
|
|
20758
20859
|
if (tableChange.volume >= 0) {
|
|
20759
20860
|
const volumeAutomation = new Automation();
|
|
@@ -20917,6 +21018,7 @@
|
|
|
20917
21018
|
this.data.readByte(); // type
|
|
20918
21019
|
IOHelper.readInt32LE(this.data); // value
|
|
20919
21020
|
const pointCount = IOHelper.readInt32LE(this.data);
|
|
21021
|
+
this._ensureLoopBoundary(pointCount, Gp3To5Importer._maxBendPointCount, 'note bend point count');
|
|
20920
21022
|
if (pointCount > 0) {
|
|
20921
21023
|
for (let i = 0; i < pointCount; i++) {
|
|
20922
21024
|
const point = new BendPoint(0, 0);
|
|
@@ -21105,25 +21207,28 @@
|
|
|
21105
21207
|
* Skips an integer (4byte) and reads a string using
|
|
21106
21208
|
* a bytesize
|
|
21107
21209
|
*/
|
|
21108
|
-
static gpReadStringIntUnused(data, encoding) {
|
|
21210
|
+
static gpReadStringIntUnused(data, encoding, maxDecodingBufferSize) {
|
|
21109
21211
|
data.skip(4);
|
|
21110
|
-
return GpBinaryHelpers.gpReadString(data, data.readByte(), encoding);
|
|
21212
|
+
return GpBinaryHelpers.gpReadString(data, data.readByte(), encoding, maxDecodingBufferSize);
|
|
21111
21213
|
}
|
|
21112
21214
|
/**
|
|
21113
21215
|
* Reads an integer as size, and then the string itself
|
|
21114
21216
|
*/
|
|
21115
|
-
static gpReadStringInt(data, encoding) {
|
|
21116
|
-
return GpBinaryHelpers.gpReadString(data, IOHelper.readInt32LE(data), encoding);
|
|
21217
|
+
static gpReadStringInt(data, encoding, maxDecodingBufferSize) {
|
|
21218
|
+
return GpBinaryHelpers.gpReadString(data, IOHelper.readInt32LE(data), encoding, maxDecodingBufferSize);
|
|
21117
21219
|
}
|
|
21118
21220
|
/**
|
|
21119
21221
|
* Reads an integer as size, skips a byte and reads the string itself
|
|
21120
21222
|
*/
|
|
21121
|
-
static gpReadStringIntByte(data, encoding) {
|
|
21223
|
+
static gpReadStringIntByte(data, encoding, maxDecodingBufferSize) {
|
|
21122
21224
|
const length = IOHelper.readInt32LE(data) - 1;
|
|
21123
21225
|
data.readByte();
|
|
21124
|
-
return GpBinaryHelpers.gpReadString(data, length, encoding);
|
|
21226
|
+
return GpBinaryHelpers.gpReadString(data, length, encoding, maxDecodingBufferSize);
|
|
21125
21227
|
}
|
|
21126
|
-
static gpReadString(data, length, encoding) {
|
|
21228
|
+
static gpReadString(data, length, encoding, maxDecodingBufferSize) {
|
|
21229
|
+
if (length > maxDecodingBufferSize) {
|
|
21230
|
+
throw new OverflowError(`Detected string exceeding maxDecodingBufferSize at offset ${data.position}`);
|
|
21231
|
+
}
|
|
21127
21232
|
const b = new Uint8Array(length);
|
|
21128
21233
|
data.read(b, 0, b.length);
|
|
21129
21234
|
return IOHelper.toString(b, encoding);
|
|
@@ -21142,12 +21247,13 @@
|
|
|
21142
21247
|
* @returns
|
|
21143
21248
|
*/
|
|
21144
21249
|
static gpReadStringByteLength(data, length, encoding) {
|
|
21250
|
+
// Fixed-width string field: 1 length byte + `length` data bytes, decoded
|
|
21251
|
+
// up to min(stringLength, length). Always consumes 1 + length bytes.
|
|
21145
21252
|
const stringLength = data.readByte();
|
|
21146
|
-
const
|
|
21147
|
-
|
|
21148
|
-
|
|
21149
|
-
|
|
21150
|
-
return s;
|
|
21253
|
+
const fieldBytes = new Uint8Array(length);
|
|
21254
|
+
data.read(fieldBytes, 0, length);
|
|
21255
|
+
const effectiveLength = Math.min(stringLength, length);
|
|
21256
|
+
return IOHelper.toString(fieldBytes.subarray(0, effectiveLength), encoding);
|
|
21151
21257
|
}
|
|
21152
21258
|
}
|
|
21153
21259
|
/**
|
|
@@ -21256,17 +21362,17 @@
|
|
|
21256
21362
|
class BinaryStylesheet {
|
|
21257
21363
|
_types = new Map();
|
|
21258
21364
|
raw = new Map();
|
|
21259
|
-
constructor(data) {
|
|
21365
|
+
constructor(data, maxDecodingBufferSize = 0) {
|
|
21260
21366
|
if (data) {
|
|
21261
|
-
this._read(data);
|
|
21367
|
+
this._read(data, maxDecodingBufferSize);
|
|
21262
21368
|
}
|
|
21263
21369
|
}
|
|
21264
|
-
_read(data) {
|
|
21370
|
+
_read(data, maxDecodingBufferSize) {
|
|
21265
21371
|
// BinaryStylesheet apears to be big-endien
|
|
21266
21372
|
const readable = ByteBuffer.fromBuffer(data);
|
|
21267
21373
|
const entryCount = IOHelper.readInt32BE(readable);
|
|
21268
21374
|
for (let i = 0; i < entryCount; i++) {
|
|
21269
|
-
const key = GpBinaryHelpers.gpReadString(readable, readable.readByte(), 'utf-8');
|
|
21375
|
+
const key = GpBinaryHelpers.gpReadString(readable, readable.readByte(), 'utf-8', maxDecodingBufferSize);
|
|
21270
21376
|
const type = readable.readByte();
|
|
21271
21377
|
this._types.set(key, type);
|
|
21272
21378
|
switch (type) {
|
|
@@ -21283,7 +21389,7 @@
|
|
|
21283
21389
|
this.addValue(key, fvalue);
|
|
21284
21390
|
break;
|
|
21285
21391
|
case DataType.String:
|
|
21286
|
-
const s = GpBinaryHelpers.gpReadString(readable, IOHelper.readInt16BE(readable), 'utf-8');
|
|
21392
|
+
const s = GpBinaryHelpers.gpReadString(readable, IOHelper.readInt16BE(readable), 'utf-8', maxDecodingBufferSize);
|
|
21287
21393
|
this.addValue(key, s);
|
|
21288
21394
|
break;
|
|
21289
21395
|
case DataType.Point:
|
|
@@ -24367,6 +24473,10 @@
|
|
|
24367
24473
|
}
|
|
24368
24474
|
// build masterbar automations
|
|
24369
24475
|
for (const [barNumber, automations] of this._masterTrackAutomations) {
|
|
24476
|
+
if (barNumber < 0 || barNumber >= this.score.masterBars.length) {
|
|
24477
|
+
// automation references a bar that is not in the score's masterBars list
|
|
24478
|
+
continue;
|
|
24479
|
+
}
|
|
24370
24480
|
const masterBar = this.score.masterBars[barNumber];
|
|
24371
24481
|
for (let i = 0, j = automations.length; i < j; i++) {
|
|
24372
24482
|
const automation = automations[i];
|
|
@@ -24656,7 +24766,7 @@
|
|
|
24656
24766
|
// at first we need to load the binary file system
|
|
24657
24767
|
// from the GPX container
|
|
24658
24768
|
Logger.debug(this.name, 'Loading ZIP entries');
|
|
24659
|
-
const fileSystem = new ZipReader(this.data);
|
|
24769
|
+
const fileSystem = new ZipReader(this.data, this.settings.importer.maxDecodingBufferSize);
|
|
24660
24770
|
let entries;
|
|
24661
24771
|
try {
|
|
24662
24772
|
entries = fileSystem.read();
|
|
@@ -24705,7 +24815,7 @@
|
|
|
24705
24815
|
const score = gpifParser.score;
|
|
24706
24816
|
if (binaryStylesheetData) {
|
|
24707
24817
|
Logger.debug(this.name, 'Start Parsing BinaryStylesheet');
|
|
24708
|
-
const stylesheet = new BinaryStylesheet(binaryStylesheetData);
|
|
24818
|
+
const stylesheet = new BinaryStylesheet(binaryStylesheetData, this.settings.importer.maxDecodingBufferSize);
|
|
24709
24819
|
stylesheet.apply(score);
|
|
24710
24820
|
Logger.debug(this.name, 'BinaryStylesheet parsed');
|
|
24711
24821
|
}
|
|
@@ -24726,15 +24836,6 @@
|
|
|
24726
24836
|
}
|
|
24727
24837
|
}
|
|
24728
24838
|
|
|
24729
|
-
/**
|
|
24730
|
-
* @internal
|
|
24731
|
-
*/
|
|
24732
|
-
class EndOfReaderError extends AlphaTabError {
|
|
24733
|
-
constructor() {
|
|
24734
|
-
super(exports.AlphaTabErrorType.Format, 'Unexpected end of data within reader');
|
|
24735
|
-
}
|
|
24736
|
-
}
|
|
24737
|
-
|
|
24738
24839
|
/**
|
|
24739
24840
|
* This utility public class allows bitwise reading of a stream
|
|
24740
24841
|
* @internal
|
|
@@ -25080,7 +25181,7 @@
|
|
|
25080
25181
|
const score = gpifParser.score;
|
|
25081
25182
|
if (binaryStylesheetData) {
|
|
25082
25183
|
Logger.debug(this.name, 'Start Parsing BinaryStylesheet');
|
|
25083
|
-
const binaryStylesheet = new BinaryStylesheet(binaryStylesheetData);
|
|
25184
|
+
const binaryStylesheet = new BinaryStylesheet(binaryStylesheetData, this.settings.importer.maxDecodingBufferSize);
|
|
25084
25185
|
binaryStylesheet.apply(score);
|
|
25085
25186
|
Logger.debug(this.name, 'BinaryStylesheet parsed');
|
|
25086
25187
|
}
|
|
@@ -25457,7 +25558,7 @@
|
|
|
25457
25558
|
return this._score;
|
|
25458
25559
|
}
|
|
25459
25560
|
_extractMusicXml() {
|
|
25460
|
-
const zip = new ZipReader(this.data);
|
|
25561
|
+
const zip = new ZipReader(this.data, this.settings.importer.maxDecodingBufferSize);
|
|
25461
25562
|
let entries;
|
|
25462
25563
|
try {
|
|
25463
25564
|
entries = zip.read();
|
|
@@ -32822,6 +32923,17 @@
|
|
|
32822
32923
|
* 
|
|
32823
32924
|
*/
|
|
32824
32925
|
beatTextAsLyrics = false;
|
|
32926
|
+
/**
|
|
32927
|
+
* This setting controls the escape hatch for handling potentially malicous or corrupt
|
|
32928
|
+
* input files. At selected spots in the codebase, we use this buffer size as maximum
|
|
32929
|
+
* allowed sizes. e.g. during unzipping or decoding strings.
|
|
32930
|
+
* This prevents resource exhaustion, especially when alphaTab is used on server side.
|
|
32931
|
+
* Increase this buffer size if you need to handle very big files.
|
|
32932
|
+
* @defaultValue `128000000`
|
|
32933
|
+
* @category Core
|
|
32934
|
+
* @since 1.9.0
|
|
32935
|
+
*/
|
|
32936
|
+
maxDecodingBufferSize = 128000000;
|
|
32825
32937
|
}
|
|
32826
32938
|
|
|
32827
32939
|
/**
|
|
@@ -34147,6 +34259,7 @@
|
|
|
34147
34259
|
o.set("encoding", obj.encoding);
|
|
34148
34260
|
o.set("mergepartgroupsinmusicxml", obj.mergePartGroupsInMusicXml);
|
|
34149
34261
|
o.set("beattextaslyrics", obj.beatTextAsLyrics);
|
|
34262
|
+
o.set("maxdecodingbuffersize", obj.maxDecodingBufferSize);
|
|
34150
34263
|
return o;
|
|
34151
34264
|
}
|
|
34152
34265
|
static setProperty(obj, property, v) {
|
|
@@ -34160,6 +34273,9 @@
|
|
|
34160
34273
|
case "beattextaslyrics":
|
|
34161
34274
|
obj.beatTextAsLyrics = v;
|
|
34162
34275
|
return true;
|
|
34276
|
+
case "maxdecodingbuffersize":
|
|
34277
|
+
obj.maxDecodingBufferSize = v;
|
|
34278
|
+
return true;
|
|
34163
34279
|
}
|
|
34164
34280
|
return false;
|
|
34165
34281
|
}
|
|
@@ -34683,12 +34799,12 @@
|
|
|
34683
34799
|
const importers = Environment.buildImporters();
|
|
34684
34800
|
Logger.debug('ScoreLoader', `Loading score from ${data.length} bytes using ${importers.length} importers`);
|
|
34685
34801
|
let score = null;
|
|
34686
|
-
const
|
|
34802
|
+
const readable = new ThrowingReadable(ByteBuffer.fromBuffer(data));
|
|
34687
34803
|
for (const importer of importers) {
|
|
34688
|
-
|
|
34804
|
+
readable.reset();
|
|
34689
34805
|
try {
|
|
34690
34806
|
Logger.debug('ScoreLoader', `Importing using importer ${importer.name}`);
|
|
34691
|
-
importer.init(
|
|
34807
|
+
importer.init(readable, settings);
|
|
34692
34808
|
score = importer.readScore();
|
|
34693
34809
|
Logger.debug('ScoreLoader', `Score imported using ${importer.name}`);
|
|
34694
34810
|
break;
|