mirror of
https://github.com/zkldi/Tachi.git
synced 2026-09-29 10:28:04 +03:00
85 lines
2.2 KiB
TypeScript
85 lines
2.2 KiB
TypeScript
import { KtLogger } from "lib/logger/logger";
|
|
|
|
export class CSVParseError extends Error {
|
|
constructor(description: string) {
|
|
super(description);
|
|
this.name = "CSVParseError";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse a "naive CSV". A Naive CSV is one that does not properly escape " or , characters.
|
|
* This also means that if konami ever have a song that has a comma, it will cause some serious problems.
|
|
*
|
|
* The reason we have a handrolled CSV parser instead of using an existing library is because eamusement CSVs are
|
|
* invalid -- due to their lack of escaping. We have to do very manual parsing to actually make this work!
|
|
*/
|
|
export function NaiveCSVParse(csvBuffer: Buffer, logger: KtLogger) {
|
|
const csvString = csvBuffer.toString("utf-8");
|
|
|
|
const csvData = csvString.split("\n");
|
|
|
|
const rawHeaders = [];
|
|
let headerLen = 0;
|
|
let curStr = "";
|
|
|
|
// looks like we're doing it like this.
|
|
for (const char of csvData[0]) {
|
|
headerLen++;
|
|
|
|
// safety checks to avoid getting DOS'd
|
|
if (headerLen > 1000) {
|
|
throw new CSVParseError("Headers were longer than 1000 characters long.");
|
|
} else if (rawHeaders.length >= 50) {
|
|
// this does not *really* do what it seems.
|
|
// because there's inevitably something left in curStr in this fn
|
|
// this means that the above check is actually > 50 headers. Not
|
|
// >= 50.
|
|
throw new CSVParseError("Too many CSV headers.");
|
|
}
|
|
|
|
if (char === ",") {
|
|
rawHeaders.push(curStr);
|
|
curStr = "";
|
|
} else {
|
|
curStr += char;
|
|
}
|
|
}
|
|
|
|
rawHeaders.push(curStr);
|
|
|
|
const rawRows = [];
|
|
|
|
for (let i = 1; i < csvData.length; i++) {
|
|
const data = csvData[i];
|
|
|
|
// @security: This should probably be safetied from DOSing
|
|
const cells = data.split(",");
|
|
|
|
// an empty string split on "," is an array with one empty value.
|
|
if (cells.length === 1) {
|
|
logger.verbose(`Skipped empty row ${i}.`);
|
|
continue;
|
|
}
|
|
|
|
if (cells.length !== rawHeaders.length) {
|
|
logger.info(
|
|
`csv has row (${i}) with invalid cell count of ${cells.length}, rejecting.`,
|
|
{
|
|
data,
|
|
}
|
|
);
|
|
throw new CSVParseError(
|
|
`Row ${i} has an invalid amount of cells (${cells.length}, expected ${rawHeaders.length}).`
|
|
);
|
|
}
|
|
|
|
rawRows.push(cells);
|
|
}
|
|
|
|
return {
|
|
rawHeaders,
|
|
rawRows,
|
|
};
|
|
}
|