Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions packages/xd-crossword-tools/src/vendor/puzjs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -458,9 +458,21 @@ function PUZtoJSON(buffer: ArrayBuffer) {

var ncol = bytes[44]
var nrow = bytes[45]
if (!ncol || !nrow) {
throw new Error(`Invalid PUZ file: header reports grid size ${ncol}x${nrow}`)
}
if (!(bytes[50] === 0 && bytes[51] === 0)) {
throw new Error("Scrambled PUZ file")
}
// Header must be followed by solution (ncol*nrow) and progress (ncol*nrow) bytes
// before the NUL-terminated string section starts. If the file is shorter than
// that the puzzle is truncated and we cannot safely decode it.
var minBytes = 52 + 2 * ncol * nrow
if (bytes.length < minBytes) {
throw new Error(
`Invalid PUZ file: header reports ${ncol}x${nrow} grid (needs at least ${minBytes} bytes) but file is only ${bytes.length} bytes`
)
}

for (var i = 0; i < nrow; i++) {
grid[i] = []
Expand All @@ -475,8 +487,8 @@ function PUZtoJSON(buffer: ArrayBuffer) {
return i < 0 || j < 0 || i >= nrow || j >= ncol || grid[i][j] === "."
}

var isAcross = []
var isDown = []
var isAcross: boolean[] = []
var isDown: boolean[] = []
var n = 0
for (var _i = 0; _i < nrow; _i++) {
for (var _j = 0; _j < ncol; _j++) {
Expand All @@ -496,8 +508,18 @@ function PUZtoJSON(buffer: ArrayBuffer) {
var ibyte = 52 + ncol * nrow * 2
function readString() {
var result = ""
if (ibyte >= bytes.length) {
throw new Error(
`Invalid PUZ file: reached end of file while reading strings. The grid scan found ${n} numbered cells (${
isAcross.filter(Boolean).length
} across, ${isDown.filter(Boolean).length} down) which doesn't match the strings stored in the file — the puzzle is likely truncated or its header grid size (${ncol}x${nrow}) is wrong.`
)
}
var b = bytes[ibyte++]
while (b !== 0) {
if (b === undefined) {
throw new Error("Invalid PUZ file: unterminated string at end of file")
}
result += String.fromCharCode(b)
b = bytes[ibyte++]
}
Expand Down
Binary file not shown.
6 changes: 6 additions & 0 deletions packages/xd-crossword-tools/tests/puz2XD.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,9 @@ it("handles greyd backgrounds", () => {
`)
// expect(xd).toMatchFile(`./tests/output/${file}.xd`)
})

it("throws a clear error for truncated/malformed puz files instead of looping forever", () => {
const path = __dirname + "/puz/truncated-strings.puz"
const puz = readFileSync(path)
expect(() => puzToXD(puz)).toThrowError(/Invalid PUZ file/)
})
Loading