Skip to content

Commit

Permalink
be more forgiving with klv length errors
Browse files Browse the repository at this point in the history
  • Loading branch information
JuanIrache committed Sep 5, 2024
1 parent 9b3ca05 commit c16cecb
Showing 1 changed file with 118 additions and 122 deletions.
240 changes: 118 additions & 122 deletions code/parseKLV.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,137 +87,133 @@ async function parseKLV(
unArrayLastChild = false;
}

if (length >= 0) {
//If empty, we still want to store the fourCC
if (length === 0) partialResult.push(undefined);
//Log unknown types for future implementation
else if (!types[ks.type]) unknown.add(ks.type);
//Recursive call to parse nested data
else if (types[ks.type].nested) {
// only if data is long enough to contain them
if (data.length >= start + 8 + length) {
const parsed = await parseKLV(data, options, {
start: start + 8,
end: start + 8 + length,
parent: ks.fourCC,
unArrayLast: unArrayLastChild,
gpsTimeSrc
});
if (parsed != null) partialResult.push(parsed);
} else partialResult.push(undefined);
}
//We can parse the Value
else if (
types[ks.type].func ||
(types[ks.type].complex && complexType)
// Warn if length is invalid
if (length < 0) {
console.warn(
'Invalid length found. Proceeding but could have errors'
);
}
//If empty, we still want to store the fourCC
if (length <= 0) partialResult.push(undefined);
//Log unknown types for future implementation
else if (!types[ks.type]) unknown.add(ks.type);
//Recursive call to parse nested data
else if (types[ks.type].nested) {
// only if data is long enough to contain them
if (data.length >= start + 8 + length) {
const parsed = await parseKLV(data, options, {
start: start + 8,
end: start + 8 + length,
parent: ks.fourCC,
unArrayLast: unArrayLastChild,
gpsTimeSrc
});
if (parsed != null) partialResult.push(parsed);
} else partialResult.push(undefined);
}
//We can parse the Value
else if (
types[ks.type].func ||
(types[ks.type].complex && complexType)
) {
//Detect data with multiple axes
let axes = 1;
if (types[ks.type].size > 1) axes = ks.size / types[ks.type].size;
//Detect them when the type is complex
else if (types[ks.type].complex && complexType.length)
axes = complexType.length;
//Human readable strings should de merged for readability
if (
types[ks.type].func === 'string' &&
ks.size === 1 &&
ks.repeat > 1
) {
//Detect data with multiple axes
let axes = 1;
if (types[ks.type].size > 1) axes = ks.size / types[ks.type].size;
//Detect them when the type is complex
else if (types[ks.type].complex && complexType.length)
axes = complexType.length;
//Human readable strings should de merged for readability
if (
types[ks.type].func === 'string' &&
ks.size === 1 &&
ks.repeat > 1
) {
ks.size = length;
ks.repeat = 1;
}
ks.size = length;
ks.repeat = 1;
}

const environment = { data, options, ks };
const specifics = { ax: axes, complexType };
const environment = { data, options, ks };
const specifics = { ax: axes, complexType };

//Access the values or single value
if (ks.repeat > 1) {
for (let i = 0; i < ks.repeat; i++)
partialResult.push(
parseV(
environment,
start + 8 + i * ks.size,
ks.size,
specifics
)
);
} else
//Access the values or single value
if (ks.repeat > 1) {
for (let i = 0; i < ks.repeat; i++)
partialResult.push(
parseV(environment, start + 8, length, specifics)
parseV(environment, start + 8 + i * ks.size, ks.size, specifics)
);
//If we just read a TYPE value, store it. Will be necessary in this nest
if (ks.fourCC === 'TYPE')
complexType = unArrayTypes(partialResult[0]);
//Abort if we are selecting devices and this one is not selected
else if (
ks.fourCC === 'DVID' &&
parent === 'DEVC' &&
options.device &&
!options.device.includes(partialResult[0])
)
return undefined;

//Something went wrong, store type for debugging
} else unknown.add(ks.type);

//Try to define unknown data based on documentation
if (
ks.fourCC === lastCC &&
generateStructArr(ks.fourCC, partialResult)
) {
//Create the string for inside the parenthesis, and remove nulls
let extraDescription = generateStructArr(
ks.fourCC,
partialResult
).filter(v => v != null);
let newValueArr = [];
//Loop partial results
partialResult.forEach((p, i) => {
//Will become the description if it's the most comprehensive one
let descCandidate = [];
let newP = [];
//Loop the keys in the description
generateStructArr(ks.fourCC, partialResult).forEach((e, ii) => {
//For nested arrays
if (Array.isArray(p) && e != null) {
//Push label and value if not null (in order to get rid of unused data)
descCandidate.push(e);
newP.push(p[ii]);
//And for values, push first label
} else if (ii === 0 && e != null) descCandidate.push(e);
});
//Save new values if worth it
if (newP.length) partialResult[i] = newP;
if (descCandidate.length > extraDescription.length)
extraDescription = descCandidate;
} else
partialResult.push(
parseV(environment, start + 8, length, specifics)
);
//If we just read a TYPE value, store it. Will be necessary in this nest
if (ks.fourCC === 'TYPE')
complexType = unArrayTypes(partialResult[0]);
//Abort if we are selecting devices and this one is not selected
else if (
ks.fourCC === 'DVID' &&
parent === 'DEVC' &&
options.device &&
!options.device.includes(partialResult[0])
)
return undefined;

//Something went wrong, store type for debugging
} else unknown.add(ks.type);

//Try to define unknown data based on documentation
if (
ks.fourCC === lastCC &&
generateStructArr(ks.fourCC, partialResult)
) {
//Create the string for inside the parenthesis, and remove nulls
let extraDescription = generateStructArr(
ks.fourCC,
partialResult
).filter(v => v != null);
let newValueArr = [];
//Loop partial results
partialResult.forEach((p, i) => {
//Will become the description if it's the most comprehensive one
let descCandidate = [];
let newP = [];
//Loop the keys in the description
generateStructArr(ks.fourCC, partialResult).forEach((e, ii) => {
//For nested arrays
if (Array.isArray(p) && e != null) {
//Push label and value if not null (in order to get rid of unused data)
descCandidate.push(e);
newP.push(p[ii]);
//And for values, push first label
} else if (ii === 0 && e != null) descCandidate.push(e);
});

if (newValueArr.length) partialResult[0] = newValueArr;

if (extraDescription.length) {
const extraDescString = extraDescription.join(',');
if (!/\(.+\)$/.test(result.STNM)) {
result.STNM = `${result.STNM || ''} (${extraDescString})`;
} else if (
result.STNM.match(/\((.+)\)$/)[1].length <
extraDescString.length
) {
result.STNM.replace(/\(.+\)$/, `(${extraDescString})`);
}
//Save new values if worth it
if (newP.length) partialResult[i] = newP;
if (descCandidate.length > extraDescription.length)
extraDescription = descCandidate;
});

if (newValueArr.length) partialResult[0] = newValueArr;

if (extraDescription.length) {
const extraDescString = extraDescription.join(',');
if (!/\(.+\)$/.test(result.STNM)) {
result.STNM = `${result.STNM || ''} (${extraDescString})`;
} else if (
result.STNM.match(/\((.+)\)$/)[1].length < extraDescString.length
) {
result.STNM.replace(/\(.+\)$/, `(${extraDescString})`);
}
}
}

//Handle data with multiple samples. Not easy
if (result.hasOwnProperty(ks.fourCC)) {
if (parent === 'STRM') {
if (!result.multi) result[ks.fourCC] = [result[ks.fourCC]];
result[ks.fourCC].push(partialResult);
result.multi = true;
} else result[ks.fourCC] = result[ks.fourCC].concat(partialResult);
} else result[ks.fourCC] = partialResult;

//Parsing error
} else throw Error('Error, negative length');
//Handle data with multiple samples. Not easy
if (result.hasOwnProperty(ks.fourCC)) {
if (parent === 'STRM') {
if (!result.multi) result[ks.fourCC] = [result[ks.fourCC]];
result[ks.fourCC].push(partialResult);
result.multi = true;
} else result[ks.fourCC] = result[ks.fourCC].concat(partialResult);
} else result[ks.fourCC] = partialResult;
}
} catch (err) {
if (options.tolerant) {
Expand Down

0 comments on commit c16cecb

Please sign in to comment.