Fix xtc chapter parser crash
This commit is contained in:
@@ -280,20 +280,24 @@ XtcError XtcParser::readChapters() {
|
||||
return XtcError::OK;
|
||||
}
|
||||
|
||||
uint64_t chapterOffset = 0;
|
||||
// chapterOffset is uint32_t in the header struct (at 0x30, followed by 4 bytes padding at 0x34)
|
||||
uint32_t chapterOffset32 = 0;
|
||||
if (!m_file.seek64(0x30)) {
|
||||
return XtcError::READ_ERROR;
|
||||
}
|
||||
if (m_file.read(reinterpret_cast<uint8_t*>(&chapterOffset), sizeof(chapterOffset)) != sizeof(chapterOffset)) {
|
||||
if (m_file.read(reinterpret_cast<uint8_t*>(&chapterOffset32), sizeof(chapterOffset32)) != sizeof(chapterOffset32)) {
|
||||
return XtcError::READ_ERROR;
|
||||
}
|
||||
const uint64_t chapterOffset = chapterOffset32;
|
||||
|
||||
if (chapterOffset == 0) {
|
||||
return XtcError::OK;
|
||||
}
|
||||
|
||||
const uint64_t fileSize = m_file.size64();
|
||||
if (chapterOffset < sizeof(XtcHeader) || chapterOffset >= fileSize || chapterOffset + 96 > fileSize) {
|
||||
// Minimum valid chapter offset: past all known metadata (title@0x38+128, author@0xB8+64 = 0xF8 = 248)
|
||||
constexpr uint64_t kMinChapterOffset = 0xF8;
|
||||
if (chapterOffset < kMinChapterOffset || chapterOffset >= fileSize || chapterOffset + 96 > fileSize) {
|
||||
return XtcError::OK;
|
||||
}
|
||||
|
||||
@@ -310,7 +314,7 @@ XtcError XtcParser::readChapters() {
|
||||
}
|
||||
|
||||
constexpr size_t chapterSize = 96;
|
||||
constexpr uint64_t kMaxChapters = 4096;
|
||||
constexpr uint64_t kMaxChapters = 200;
|
||||
const uint64_t available = maxOffset - chapterOffset;
|
||||
const uint64_t rawCount = available / chapterSize;
|
||||
const size_t chapterCount = static_cast<size_t>(rawCount > kMaxChapters ? kMaxChapters : rawCount);
|
||||
|
||||
@@ -248,8 +248,10 @@ void XtcReaderActivity::renderPage() {
|
||||
renderer.clearScreen();
|
||||
|
||||
// Copy page bitmap using GfxRenderer's drawPixel
|
||||
// XTC/XTCH pages are pre-rendered with status bar included, so render full page
|
||||
const uint16_t maxSrcY = pageHeight;
|
||||
// XTC/XTCH pages are pre-rendered with status bar included, so render full page.
|
||||
// Clamp to display bounds: some files are encoded for a different screen size.
|
||||
const uint16_t maxSrcX = std::min(pageWidth, static_cast<uint16_t>(renderer.getScreenWidth()));
|
||||
const uint16_t maxSrcY = std::min(pageHeight, static_cast<uint16_t>(renderer.getScreenHeight()));
|
||||
|
||||
if (bitDepth == 2) {
|
||||
// XTH 2-bit mode: Two bit planes, column-major order
|
||||
@@ -280,8 +282,8 @@ void XtcReaderActivity::renderPage() {
|
||||
|
||||
// Count pixel distribution for debugging
|
||||
uint32_t pixelCounts[4] = {0, 0, 0, 0};
|
||||
for (uint16_t y = 0; y < pageHeight; y++) {
|
||||
for (uint16_t x = 0; x < pageWidth; x++) {
|
||||
for (uint16_t y = 0; y < maxSrcY; y++) {
|
||||
for (uint16_t x = 0; x < maxSrcX; x++) {
|
||||
pixelCounts[getPixelValue(x, y)]++;
|
||||
}
|
||||
}
|
||||
@@ -289,8 +291,8 @@ void XtcReaderActivity::renderPage() {
|
||||
pixelCounts[1], pixelCounts[2], pixelCounts[3]);
|
||||
|
||||
// Pass 1: BW buffer - draw all non-white pixels as black
|
||||
for (uint16_t y = 0; y < pageHeight; y++) {
|
||||
for (uint16_t x = 0; x < pageWidth; x++) {
|
||||
for (uint16_t y = 0; y < maxSrcY; y++) {
|
||||
for (uint16_t x = 0; x < maxSrcX; x++) {
|
||||
if (getPixelValue(x, y) >= 1) {
|
||||
renderer.drawPixel(x, y, true);
|
||||
}
|
||||
@@ -309,8 +311,8 @@ void XtcReaderActivity::renderPage() {
|
||||
// Pass 2: LSB buffer - mark DARK gray only (XTH value 1)
|
||||
// In LUT: 0 bit = apply gray effect, 1 bit = untouched
|
||||
renderer.clearScreen(0x00);
|
||||
for (uint16_t y = 0; y < pageHeight; y++) {
|
||||
for (uint16_t x = 0; x < pageWidth; x++) {
|
||||
for (uint16_t y = 0; y < maxSrcY; y++) {
|
||||
for (uint16_t x = 0; x < maxSrcX; x++) {
|
||||
if (getPixelValue(x, y) == 1) { // Dark grey only
|
||||
renderer.drawPixel(x, y, false);
|
||||
}
|
||||
@@ -321,8 +323,8 @@ void XtcReaderActivity::renderPage() {
|
||||
// Pass 3: MSB buffer - mark LIGHT AND DARK gray (XTH value 1 or 2)
|
||||
// In LUT: 0 bit = apply gray effect, 1 bit = untouched
|
||||
renderer.clearScreen(0x00);
|
||||
for (uint16_t y = 0; y < pageHeight; y++) {
|
||||
for (uint16_t x = 0; x < pageWidth; x++) {
|
||||
for (uint16_t y = 0; y < maxSrcY; y++) {
|
||||
for (uint16_t x = 0; x < maxSrcX; x++) {
|
||||
const uint8_t pv = getPixelValue(x, y);
|
||||
if (pv == 1 || pv == 2) { // Dark grey or Light grey
|
||||
renderer.drawPixel(x, y, false);
|
||||
@@ -336,8 +338,8 @@ void XtcReaderActivity::renderPage() {
|
||||
|
||||
// Pass 4: Re-render BW to framebuffer (restore for next frame, instead of restoreBwBuffer)
|
||||
renderer.clearScreen();
|
||||
for (uint16_t y = 0; y < pageHeight; y++) {
|
||||
for (uint16_t x = 0; x < pageWidth; x++) {
|
||||
for (uint16_t y = 0; y < maxSrcY; y++) {
|
||||
for (uint16_t x = 0; x < maxSrcX; x++) {
|
||||
if (getPixelValue(x, y) >= 1) {
|
||||
renderer.drawPixel(x, y, true);
|
||||
}
|
||||
@@ -358,7 +360,7 @@ void XtcReaderActivity::renderPage() {
|
||||
for (uint16_t srcY = 0; srcY < maxSrcY; srcY++) {
|
||||
const size_t srcRowStart = srcY * srcRowBytes;
|
||||
|
||||
for (uint16_t srcX = 0; srcX < pageWidth; srcX++) {
|
||||
for (uint16_t srcX = 0; srcX < maxSrcX; srcX++) {
|
||||
// Read source pixel (MSB first, bit 7 = leftmost pixel)
|
||||
const size_t srcByte = srcRowStart + srcX / 8;
|
||||
const size_t srcBit = 7 - (srcX % 8);
|
||||
@@ -461,14 +463,17 @@ bool XtcReaderActivity::drawCurrentPageToBuffer(const std::string& filePath, Gfx
|
||||
|
||||
renderer.clearScreen();
|
||||
|
||||
const uint16_t maxX = std::min(pageWidth, static_cast<uint16_t>(renderer.getScreenWidth()));
|
||||
const uint16_t maxY = std::min(pageHeight, static_cast<uint16_t>(renderer.getScreenHeight()));
|
||||
|
||||
if (bitDepth == 2) {
|
||||
// 2-bit XTH: draw all non-white pixels as black (BW pass only)
|
||||
const size_t planeSize = (static_cast<size_t>(pageWidth) * pageHeight + 7) / 8;
|
||||
const uint8_t* plane1 = pageBuffer;
|
||||
const uint8_t* plane2 = pageBuffer + planeSize;
|
||||
const size_t colBytes = (pageHeight + 7) / 8;
|
||||
for (uint16_t y = 0; y < pageHeight; y++) {
|
||||
for (uint16_t x = 0; x < pageWidth; x++) {
|
||||
for (uint16_t y = 0; y < maxY; y++) {
|
||||
for (uint16_t x = 0; x < maxX; x++) {
|
||||
const size_t colIndex = pageWidth - 1 - x;
|
||||
const size_t byteInCol = y / 8;
|
||||
const size_t bitInByte = 7 - (y % 8);
|
||||
@@ -483,8 +488,8 @@ bool XtcReaderActivity::drawCurrentPageToBuffer(const std::string& filePath, Gfx
|
||||
} else {
|
||||
// 1-bit XTG: draw black pixels
|
||||
const size_t srcRowBytes = (pageWidth + 7) / 8;
|
||||
for (uint16_t srcY = 0; srcY < pageHeight; srcY++) {
|
||||
for (uint16_t srcX = 0; srcX < pageWidth; srcX++) {
|
||||
for (uint16_t srcY = 0; srcY < maxY; srcY++) {
|
||||
for (uint16_t srcX = 0; srcX < maxX; srcX++) {
|
||||
const bool isBlack = !((pageBuffer[srcY * srcRowBytes + srcX / 8] >> (7 - srcX % 8)) & 1);
|
||||
if (isBlack) renderer.drawPixel(srcX, srcY, true);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user