perf: Avoid creating strings for file extension checks (#1303)
## Summary **What is the goal of this PR?** This change avoids the pattern of creating a `std::string` using `.substr` in order to compare against a file extension literal. ```c++ std::string path; if (path.length() >= 4 && path.substr(path.length() - 4) == ".ext") ``` The `checkFileExtension` utility has moved from StringUtils to FsHelpers, to be available to code in lib/. The signature now accepts a `std::string_view` instead of `std::string`, which makes the single implementation reusable for Arduino `String`. Added utility functions for commonly repeated extensions. These changes **save about 2 KB of flash (5,999,427 to 5,997,343)**. --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? _**NO**_
This commit is contained in:
@@ -103,16 +103,13 @@ bool Epub::parseContentOpf(BookMetadataCache::BookMetadata& bookMetadata) {
|
|||||||
pos += strlen(pattern);
|
pos += strlen(pattern);
|
||||||
const auto endPos = coverPageHtml.find('"', pos);
|
const auto endPos = coverPageHtml.find('"', pos);
|
||||||
if (endPos != std::string::npos) {
|
if (endPos != std::string::npos) {
|
||||||
const auto ref = coverPageHtml.substr(pos, endPos - pos);
|
const auto ref = std::string_view{coverPageHtml}.substr(pos, endPos - pos);
|
||||||
// Check if it's an image file
|
// Check if it's an image file
|
||||||
if (ref.length() >= 4) {
|
if (FsHelpers::hasPngExtension(ref) || FsHelpers::hasJpgExtension(ref) || FsHelpers::hasGifExtension(ref)) {
|
||||||
const auto ext = ref.substr(ref.length() - 4);
|
|
||||||
if (ext == ".png" || ext == ".jpg" || ext == "jpeg" || ext == ".gif") {
|
|
||||||
imageRef = ref;
|
imageRef = ref;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
pos = coverPageHtml.find(pattern, pos);
|
pos = coverPageHtml.find(pattern, pos);
|
||||||
}
|
}
|
||||||
if (!imageRef.empty()) break;
|
if (!imageRef.empty()) break;
|
||||||
@@ -541,8 +538,7 @@ bool Epub::generateCoverBmp(bool cropped) const {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (coverImageHref.substr(coverImageHref.length() - 4) == ".jpg" ||
|
if (FsHelpers::hasJpgExtension(coverImageHref)) {
|
||||||
coverImageHref.substr(coverImageHref.length() - 5) == ".jpeg") {
|
|
||||||
LOG_DBG("EBP", "Generating BMP from JPG cover image (%s mode)", cropped ? "cropped" : "fit");
|
LOG_DBG("EBP", "Generating BMP from JPG cover image (%s mode)", cropped ? "cropped" : "fit");
|
||||||
const auto coverJpgTempPath = getCachePath() + "/.cover.jpg";
|
const auto coverJpgTempPath = getCachePath() + "/.cover.jpg";
|
||||||
|
|
||||||
@@ -575,7 +571,7 @@ bool Epub::generateCoverBmp(bool cropped) const {
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (coverImageHref.substr(coverImageHref.length() - 4) == ".png") {
|
if (FsHelpers::hasPngExtension(coverImageHref)) {
|
||||||
LOG_DBG("EBP", "Generating BMP from PNG cover image (%s mode)", cropped ? "cropped" : "fit");
|
LOG_DBG("EBP", "Generating BMP from PNG cover image (%s mode)", cropped ? "cropped" : "fit");
|
||||||
const auto coverPngTempPath = getCachePath() + "/.cover.png";
|
const auto coverPngTempPath = getCachePath() + "/.cover.png";
|
||||||
|
|
||||||
@@ -629,8 +625,7 @@ bool Epub::generateThumbBmp(int height) const {
|
|||||||
const auto coverImageHref = bookMetadataCache->coreMetadata.coverItemHref;
|
const auto coverImageHref = bookMetadataCache->coreMetadata.coverItemHref;
|
||||||
if (coverImageHref.empty()) {
|
if (coverImageHref.empty()) {
|
||||||
LOG_DBG("EBP", "No known cover image for thumbnail");
|
LOG_DBG("EBP", "No known cover image for thumbnail");
|
||||||
} else if (coverImageHref.substr(coverImageHref.length() - 4) == ".jpg" ||
|
} else if (FsHelpers::hasJpgExtension(coverImageHref)) {
|
||||||
coverImageHref.substr(coverImageHref.length() - 5) == ".jpeg") {
|
|
||||||
LOG_DBG("EBP", "Generating thumb BMP from JPG cover image");
|
LOG_DBG("EBP", "Generating thumb BMP from JPG cover image");
|
||||||
const auto coverJpgTempPath = getCachePath() + "/.cover.jpg";
|
const auto coverJpgTempPath = getCachePath() + "/.cover.jpg";
|
||||||
|
|
||||||
@@ -666,7 +661,7 @@ bool Epub::generateThumbBmp(int height) const {
|
|||||||
}
|
}
|
||||||
LOG_DBG("EBP", "Generated thumb BMP from JPG cover image, success: %s", success ? "yes" : "no");
|
LOG_DBG("EBP", "Generated thumb BMP from JPG cover image, success: %s", success ? "yes" : "no");
|
||||||
return success;
|
return success;
|
||||||
} else if (coverImageHref.substr(coverImageHref.length() - 4) == ".png") {
|
} else if (FsHelpers::hasPngExtension(coverImageHref)) {
|
||||||
LOG_DBG("EBP", "Generating thumb BMP from PNG cover image");
|
LOG_DBG("EBP", "Generating thumb BMP from PNG cover image");
|
||||||
const auto coverPngTempPath = getCachePath() + "/.cover.png";
|
const auto coverPngTempPath = getCachePath() + "/.cover.png";
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "JpegToFramebufferConverter.h"
|
#include "JpegToFramebufferConverter.h"
|
||||||
|
|
||||||
|
#include <FsHelpers.h>
|
||||||
#include <GfxRenderer.h>
|
#include <GfxRenderer.h>
|
||||||
#include <HalStorage.h>
|
#include <HalStorage.h>
|
||||||
#include <JPEGDEC.h>
|
#include <JPEGDEC.h>
|
||||||
@@ -486,9 +487,5 @@ bool JpegToFramebufferConverter::decodeToFramebuffer(const std::string& imagePat
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool JpegToFramebufferConverter::supportsFormat(const std::string& extension) {
|
bool JpegToFramebufferConverter::supportsFormat(const std::string& extension) {
|
||||||
std::string ext = extension;
|
return FsHelpers::hasJpgExtension(extension);
|
||||||
for (auto& c : ext) {
|
|
||||||
c = tolower(c);
|
|
||||||
}
|
|
||||||
return (ext == ".jpg" || ext == ".jpeg");
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "PngToFramebufferConverter.h"
|
#include "PngToFramebufferConverter.h"
|
||||||
|
|
||||||
|
#include <FsHelpers.h>
|
||||||
#include <GfxRenderer.h>
|
#include <GfxRenderer.h>
|
||||||
#include <HalStorage.h>
|
#include <HalStorage.h>
|
||||||
#include <Logging.h>
|
#include <Logging.h>
|
||||||
@@ -391,9 +392,5 @@ bool PngToFramebufferConverter::decodeToFramebuffer(const std::string& imagePath
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool PngToFramebufferConverter::supportsFormat(const std::string& extension) {
|
bool PngToFramebufferConverter::supportsFormat(const std::string& extension) {
|
||||||
std::string ext = extension;
|
return FsHelpers::hasPngExtension(extension);
|
||||||
for (auto& c : ext) {
|
|
||||||
c = tolower(c);
|
|
||||||
}
|
|
||||||
return (ext == ".png");
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
#include "FsHelpers.h"
|
#include "FsHelpers.h"
|
||||||
|
|
||||||
|
#include <cctype>
|
||||||
|
#include <cstring>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
std::string FsHelpers::normalisePath(const std::string& path) {
|
namespace FsHelpers {
|
||||||
|
|
||||||
|
std::string normalisePath(const std::string& path) {
|
||||||
std::vector<std::string> components;
|
std::vector<std::string> components;
|
||||||
std::string component;
|
std::string component;
|
||||||
|
|
||||||
@@ -37,3 +41,41 @@ std::string FsHelpers::normalisePath(const std::string& path) {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool checkFileExtension(std::string_view fileName, const char* extension) {
|
||||||
|
const size_t extLen = strlen(extension);
|
||||||
|
if (fileName.length() < extLen) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t offset = fileName.length() - extLen;
|
||||||
|
for (size_t i = 0; i < extLen; i++) {
|
||||||
|
if (tolower(static_cast<unsigned char>(fileName[offset + i])) !=
|
||||||
|
tolower(static_cast<unsigned char>(extension[i]))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasJpgExtension(std::string_view fileName) {
|
||||||
|
return checkFileExtension(fileName, ".jpg") || checkFileExtension(fileName, ".jpeg");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasPngExtension(std::string_view fileName) { return checkFileExtension(fileName, ".png"); }
|
||||||
|
|
||||||
|
bool hasBmpExtension(std::string_view fileName) { return checkFileExtension(fileName, ".bmp"); }
|
||||||
|
|
||||||
|
bool hasGifExtension(std::string_view fileName) { return checkFileExtension(fileName, ".gif"); }
|
||||||
|
|
||||||
|
bool hasEpubExtension(std::string_view fileName) { return checkFileExtension(fileName, ".epub"); }
|
||||||
|
|
||||||
|
bool hasXtcExtension(std::string_view fileName) {
|
||||||
|
return checkFileExtension(fileName, ".xtc") || checkFileExtension(fileName, ".xtch");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasTxtExtension(std::string_view fileName) { return checkFileExtension(fileName, ".txt"); }
|
||||||
|
|
||||||
|
bool hasMarkdownExtension(std::string_view fileName) { return checkFileExtension(fileName, ".md"); }
|
||||||
|
|
||||||
|
} // namespace FsHelpers
|
||||||
|
|||||||
@@ -1,7 +1,58 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <string>
|
#include <WString.h>
|
||||||
|
|
||||||
class FsHelpers {
|
#include <string>
|
||||||
public:
|
#include <string_view>
|
||||||
static std::string normalisePath(const std::string& path);
|
|
||||||
};
|
namespace FsHelpers {
|
||||||
|
|
||||||
|
std::string normalisePath(const std::string& path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the given filename ends with the specified extension (case-insensitive).
|
||||||
|
*/
|
||||||
|
bool checkFileExtension(std::string_view fileName, const char* extension);
|
||||||
|
inline bool checkFileExtension(const String& fileName, const char* extension) {
|
||||||
|
return checkFileExtension(std::string_view{fileName.c_str(), fileName.length()}, extension);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for either .jpg or .jpeg extension (case-insensitive)
|
||||||
|
bool hasJpgExtension(std::string_view fileName);
|
||||||
|
inline bool hasJpgExtension(const String& fileName) {
|
||||||
|
return hasJpgExtension(std::string_view{fileName.c_str(), fileName.length()});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for .png extension (case-insensitive)
|
||||||
|
bool hasPngExtension(std::string_view fileName);
|
||||||
|
inline bool hasPngExtension(const String& fileName) {
|
||||||
|
return hasPngExtension(std::string_view{fileName.c_str(), fileName.length()});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for .bmp extension (case-insensitive)
|
||||||
|
bool hasBmpExtension(std::string_view fileName);
|
||||||
|
|
||||||
|
// Check for .gif extension (case-insensitive)
|
||||||
|
bool hasGifExtension(std::string_view fileName);
|
||||||
|
inline bool hasGifExtension(const String& fileName) {
|
||||||
|
return hasGifExtension(std::string_view{fileName.c_str(), fileName.length()});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for .epub extension (case-insensitive)
|
||||||
|
bool hasEpubExtension(std::string_view fileName);
|
||||||
|
inline bool hasEpubExtension(const String& fileName) {
|
||||||
|
return hasEpubExtension(std::string_view{fileName.c_str(), fileName.length()});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for either .xtc or .xtch extension (case-insensitive)
|
||||||
|
bool hasXtcExtension(std::string_view fileName);
|
||||||
|
|
||||||
|
// Check for .txt extension (case-insensitive)
|
||||||
|
bool hasTxtExtension(std::string_view fileName);
|
||||||
|
inline bool hasTxtExtension(const String& fileName) {
|
||||||
|
return hasTxtExtension(std::string_view{fileName.c_str(), fileName.length()});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for .md extension (case-insensitive)
|
||||||
|
bool hasMarkdownExtension(std::string_view fileName);
|
||||||
|
|
||||||
|
} // namespace FsHelpers
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ std::string Txt::getTitle() const {
|
|||||||
std::string filename = (lastSlash != std::string::npos) ? filepath.substr(lastSlash + 1) : filepath;
|
std::string filename = (lastSlash != std::string::npos) ? filepath.substr(lastSlash + 1) : filepath;
|
||||||
|
|
||||||
// Remove .txt extension
|
// Remove .txt extension
|
||||||
if (filename.length() >= 4 && filename.substr(filename.length() - 4) == ".txt") {
|
if (FsHelpers::hasTxtExtension(filename)) {
|
||||||
filename = filename.substr(0, filename.length() - 4);
|
filename = filename.substr(0, filename.length() - 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,14 +112,7 @@ bool Txt::generateCoverBmp() const {
|
|||||||
// Setup cache directory
|
// Setup cache directory
|
||||||
setupCacheDir();
|
setupCacheDir();
|
||||||
|
|
||||||
// Get file extension
|
if (FsHelpers::hasBmpExtension(coverImagePath)) {
|
||||||
const size_t len = coverImagePath.length();
|
|
||||||
const bool isJpg =
|
|
||||||
(len >= 4 && (coverImagePath.substr(len - 4) == ".jpg" || coverImagePath.substr(len - 4) == ".JPG")) ||
|
|
||||||
(len >= 5 && (coverImagePath.substr(len - 5) == ".jpeg" || coverImagePath.substr(len - 5) == ".JPEG"));
|
|
||||||
const bool isBmp = len >= 4 && (coverImagePath.substr(len - 4) == ".bmp" || coverImagePath.substr(len - 4) == ".BMP");
|
|
||||||
|
|
||||||
if (isBmp) {
|
|
||||||
// Copy BMP file to cache
|
// Copy BMP file to cache
|
||||||
LOG_DBG("TXT", "Copying BMP cover image to cache");
|
LOG_DBG("TXT", "Copying BMP cover image to cache");
|
||||||
FsFile src, dst;
|
FsFile src, dst;
|
||||||
@@ -139,9 +132,7 @@ bool Txt::generateCoverBmp() const {
|
|||||||
dst.close();
|
dst.close();
|
||||||
LOG_DBG("TXT", "Copied BMP cover to cache");
|
LOG_DBG("TXT", "Copied BMP cover to cache");
|
||||||
return true;
|
return true;
|
||||||
}
|
} else if (FsHelpers::hasJpgExtension(coverImagePath)) {
|
||||||
|
|
||||||
if (isJpg) {
|
|
||||||
// Convert JPG/JPEG to BMP (same approach as Epub)
|
// Convert JPG/JPEG to BMP (same approach as Epub)
|
||||||
LOG_DBG("TXT", "Generating BMP from JPG cover image");
|
LOG_DBG("TXT", "Generating BMP from JPG cover image");
|
||||||
FsFile coverJpg, coverBmp;
|
FsFile coverJpg, coverBmp;
|
||||||
|
|||||||
@@ -144,14 +144,4 @@ inline const char* errorToString(XtcError err) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if filename has XTC/XTCH extension
|
|
||||||
*/
|
|
||||||
inline bool isXtcExtension(const char* filename) {
|
|
||||||
if (!filename) return false;
|
|
||||||
const char* ext = strrchr(filename, '.');
|
|
||||||
if (!ext) return false;
|
|
||||||
return (strcasecmp(ext, ".xtc") == 0 || strcasecmp(ext, ".xtch") == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace xtc
|
} // namespace xtc
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "RecentBooksStore.h"
|
#include "RecentBooksStore.h"
|
||||||
|
|
||||||
#include <Epub.h>
|
#include <Epub.h>
|
||||||
|
#include <FsHelpers.h>
|
||||||
#include <HalStorage.h>
|
#include <HalStorage.h>
|
||||||
#include <JsonSettingsIO.h>
|
#include <JsonSettingsIO.h>
|
||||||
#include <Logging.h>
|
#include <Logging.h>
|
||||||
@@ -9,8 +10,6 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include "util/StringUtils.h"
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr uint8_t RECENT_BOOKS_FILE_VERSION = 3;
|
constexpr uint8_t RECENT_BOOKS_FILE_VERSION = 3;
|
||||||
constexpr char RECENT_BOOKS_FILE_BIN[] = "/.crosspoint/recent.bin";
|
constexpr char RECENT_BOOKS_FILE_BIN[] = "/.crosspoint/recent.bin";
|
||||||
@@ -71,19 +70,17 @@ RecentBook RecentBooksStore::getDataFromBook(std::string path) const {
|
|||||||
// If epub, try to load the metadata for title/author and cover.
|
// If epub, try to load the metadata for title/author and cover.
|
||||||
// Use buildIfMissing=false to avoid heavy epub loading on boot; getTitle()/getAuthor() may be
|
// Use buildIfMissing=false to avoid heavy epub loading on boot; getTitle()/getAuthor() may be
|
||||||
// blank until the book is opened, and entries with missing title are omitted from recent list.
|
// blank until the book is opened, and entries with missing title are omitted from recent list.
|
||||||
if (StringUtils::checkFileExtension(lastBookFileName, ".epub")) {
|
if (FsHelpers::hasEpubExtension(lastBookFileName)) {
|
||||||
Epub epub(path, "/.crosspoint");
|
Epub epub(path, "/.crosspoint");
|
||||||
epub.load(false, true);
|
epub.load(false, true);
|
||||||
return RecentBook{path, epub.getTitle(), epub.getAuthor(), epub.getThumbBmpPath()};
|
return RecentBook{path, epub.getTitle(), epub.getAuthor(), epub.getThumbBmpPath()};
|
||||||
} else if (StringUtils::checkFileExtension(lastBookFileName, ".xtch") ||
|
} else if (FsHelpers::hasXtcExtension(lastBookFileName)) {
|
||||||
StringUtils::checkFileExtension(lastBookFileName, ".xtc")) {
|
|
||||||
// Handle XTC file
|
// Handle XTC file
|
||||||
Xtc xtc(path, "/.crosspoint");
|
Xtc xtc(path, "/.crosspoint");
|
||||||
if (xtc.load()) {
|
if (xtc.load()) {
|
||||||
return RecentBook{path, xtc.getTitle(), xtc.getAuthor(), xtc.getThumbBmpPath()};
|
return RecentBook{path, xtc.getTitle(), xtc.getAuthor(), xtc.getThumbBmpPath()};
|
||||||
}
|
}
|
||||||
} else if (StringUtils::checkFileExtension(lastBookFileName, ".txt") ||
|
} else if (FsHelpers::hasTxtExtension(lastBookFileName) || FsHelpers::hasMarkdownExtension(lastBookFileName)) {
|
||||||
StringUtils::checkFileExtension(lastBookFileName, ".md")) {
|
|
||||||
return RecentBook{path, lastBookFileName, "", ""};
|
return RecentBook{path, lastBookFileName, "", ""};
|
||||||
}
|
}
|
||||||
return RecentBook{path, "", "", ""};
|
return RecentBook{path, "", "", ""};
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "SleepActivity.h"
|
#include "SleepActivity.h"
|
||||||
|
|
||||||
#include <Epub.h>
|
#include <Epub.h>
|
||||||
|
#include <FsHelpers.h>
|
||||||
#include <GfxRenderer.h>
|
#include <GfxRenderer.h>
|
||||||
#include <HalStorage.h>
|
#include <HalStorage.h>
|
||||||
#include <I18n.h>
|
#include <I18n.h>
|
||||||
@@ -12,7 +13,6 @@
|
|||||||
#include "components/UITheme.h"
|
#include "components/UITheme.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
#include "images/Logo120.h"
|
#include "images/Logo120.h"
|
||||||
#include "util/StringUtils.h"
|
|
||||||
|
|
||||||
void SleepActivity::onEnter() {
|
void SleepActivity::onEnter() {
|
||||||
Activity::onEnter();
|
Activity::onEnter();
|
||||||
@@ -61,7 +61,7 @@ void SleepActivity::renderCustomSleepScreen() const {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filename.substr(filename.length() - 4) != ".bmp") {
|
if (!FsHelpers::hasBmpExtension(filename)) {
|
||||||
LOG_DBG("SLP", "Skipping non-.bmp file name: %s", name);
|
LOG_DBG("SLP", "Skipping non-.bmp file name: %s", name);
|
||||||
file.close();
|
file.close();
|
||||||
continue;
|
continue;
|
||||||
@@ -228,8 +228,7 @@ void SleepActivity::renderCoverSleepScreen() const {
|
|||||||
bool cropped = SETTINGS.sleepScreenCoverMode == CrossPointSettings::SLEEP_SCREEN_COVER_MODE::CROP;
|
bool cropped = SETTINGS.sleepScreenCoverMode == CrossPointSettings::SLEEP_SCREEN_COVER_MODE::CROP;
|
||||||
|
|
||||||
// Check if the current book is XTC, TXT, or EPUB
|
// Check if the current book is XTC, TXT, or EPUB
|
||||||
if (StringUtils::checkFileExtension(APP_STATE.openEpubPath, ".xtc") ||
|
if (FsHelpers::hasXtcExtension(APP_STATE.openEpubPath)) {
|
||||||
StringUtils::checkFileExtension(APP_STATE.openEpubPath, ".xtch")) {
|
|
||||||
// Handle XTC file
|
// Handle XTC file
|
||||||
Xtc lastXtc(APP_STATE.openEpubPath, "/.crosspoint");
|
Xtc lastXtc(APP_STATE.openEpubPath, "/.crosspoint");
|
||||||
if (!lastXtc.load()) {
|
if (!lastXtc.load()) {
|
||||||
@@ -243,7 +242,7 @@ void SleepActivity::renderCoverSleepScreen() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
coverBmpPath = lastXtc.getCoverBmpPath();
|
coverBmpPath = lastXtc.getCoverBmpPath();
|
||||||
} else if (StringUtils::checkFileExtension(APP_STATE.openEpubPath, ".txt")) {
|
} else if (FsHelpers::hasTxtExtension(APP_STATE.openEpubPath)) {
|
||||||
// Handle TXT file - looks for cover image in the same folder
|
// Handle TXT file - looks for cover image in the same folder
|
||||||
Txt lastTxt(APP_STATE.openEpubPath, "/.crosspoint");
|
Txt lastTxt(APP_STATE.openEpubPath, "/.crosspoint");
|
||||||
if (!lastTxt.load()) {
|
if (!lastTxt.load()) {
|
||||||
@@ -257,7 +256,7 @@ void SleepActivity::renderCoverSleepScreen() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
coverBmpPath = lastTxt.getCoverBmpPath();
|
coverBmpPath = lastTxt.getCoverBmpPath();
|
||||||
} else if (StringUtils::checkFileExtension(APP_STATE.openEpubPath, ".epub")) {
|
} else if (FsHelpers::hasEpubExtension(APP_STATE.openEpubPath)) {
|
||||||
// Handle EPUB file
|
// Handle EPUB file
|
||||||
Epub lastEpub(APP_STATE.openEpubPath, "/.crosspoint");
|
Epub lastEpub(APP_STATE.openEpubPath, "/.crosspoint");
|
||||||
// Skip loading css since we only need metadata here
|
// Skip loading css since we only need metadata here
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "FileBrowserActivity.h"
|
#include "FileBrowserActivity.h"
|
||||||
|
|
||||||
#include <Epub.h>
|
#include <Epub.h>
|
||||||
|
#include <FsHelpers.h>
|
||||||
#include <GfxRenderer.h>
|
#include <GfxRenderer.h>
|
||||||
#include <HalStorage.h>
|
#include <HalStorage.h>
|
||||||
#include <I18n.h>
|
#include <I18n.h>
|
||||||
@@ -11,7 +12,6 @@
|
|||||||
#include "MappedInputManager.h"
|
#include "MappedInputManager.h"
|
||||||
#include "components/UITheme.h"
|
#include "components/UITheme.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
#include "util/StringUtils.h"
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr unsigned long GO_HOME_MS = 1000;
|
constexpr unsigned long GO_HOME_MS = 1000;
|
||||||
@@ -91,10 +91,10 @@ void FileBrowserActivity::loadFiles() {
|
|||||||
if (file.isDirectory()) {
|
if (file.isDirectory()) {
|
||||||
files.emplace_back(std::string(name) + "/");
|
files.emplace_back(std::string(name) + "/");
|
||||||
} else {
|
} else {
|
||||||
auto filename = std::string(name);
|
std::string_view filename{name};
|
||||||
if (StringUtils::checkFileExtension(filename, ".epub") || StringUtils::checkFileExtension(filename, ".xtch") ||
|
if (FsHelpers::hasEpubExtension(filename) || FsHelpers::hasXtcExtension(filename) ||
|
||||||
StringUtils::checkFileExtension(filename, ".xtc") || StringUtils::checkFileExtension(filename, ".txt") ||
|
FsHelpers::hasTxtExtension(filename) || FsHelpers::hasMarkdownExtension(filename) ||
|
||||||
StringUtils::checkFileExtension(filename, ".md") || StringUtils::checkFileExtension(filename, ".bmp")) {
|
FsHelpers::hasBmpExtension(filename)) {
|
||||||
files.emplace_back(filename);
|
files.emplace_back(filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,7 +120,7 @@ void FileBrowserActivity::onExit() {
|
|||||||
|
|
||||||
void FileBrowserActivity::clearFileMetadata(const std::string& fullPath) {
|
void FileBrowserActivity::clearFileMetadata(const std::string& fullPath) {
|
||||||
// Only clear cache for .epub files
|
// Only clear cache for .epub files
|
||||||
if (StringUtils::checkFileExtension(fullPath, ".epub")) {
|
if (FsHelpers::hasEpubExtension(fullPath)) {
|
||||||
Epub(fullPath, "/.crosspoint").clearCache();
|
Epub(fullPath, "/.crosspoint").clearCache();
|
||||||
LOG_DBG("FileBrowser", "Cleared metadata cache for: %s", fullPath.c_str());
|
LOG_DBG("FileBrowser", "Cleared metadata cache for: %s", fullPath.c_str());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <Bitmap.h>
|
#include <Bitmap.h>
|
||||||
#include <Epub.h>
|
#include <Epub.h>
|
||||||
|
#include <FsHelpers.h>
|
||||||
#include <GfxRenderer.h>
|
#include <GfxRenderer.h>
|
||||||
#include <HalStorage.h>
|
#include <HalStorage.h>
|
||||||
#include <I18n.h>
|
#include <I18n.h>
|
||||||
@@ -17,7 +18,6 @@
|
|||||||
#include "RecentBooksStore.h"
|
#include "RecentBooksStore.h"
|
||||||
#include "components/UITheme.h"
|
#include "components/UITheme.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
#include "util/StringUtils.h"
|
|
||||||
|
|
||||||
int HomeActivity::getMenuItemCount() const {
|
int HomeActivity::getMenuItemCount() const {
|
||||||
int count = 4; // File Browser, Recents, File transfer, Settings
|
int count = 4; // File Browser, Recents, File transfer, Settings
|
||||||
@@ -61,7 +61,7 @@ void HomeActivity::loadRecentCovers(int coverHeight) {
|
|||||||
std::string coverPath = UITheme::getCoverThumbPath(book.coverBmpPath, coverHeight);
|
std::string coverPath = UITheme::getCoverThumbPath(book.coverBmpPath, coverHeight);
|
||||||
if (!Storage.exists(coverPath.c_str())) {
|
if (!Storage.exists(coverPath.c_str())) {
|
||||||
// If epub, try to load the metadata for title/author and cover
|
// If epub, try to load the metadata for title/author and cover
|
||||||
if (StringUtils::checkFileExtension(book.path, ".epub")) {
|
if (FsHelpers::hasEpubExtension(book.path)) {
|
||||||
Epub epub(book.path, "/.crosspoint");
|
Epub epub(book.path, "/.crosspoint");
|
||||||
// Skip loading css since we only need metadata here
|
// Skip loading css since we only need metadata here
|
||||||
epub.load(false, true);
|
epub.load(false, true);
|
||||||
@@ -79,8 +79,7 @@ void HomeActivity::loadRecentCovers(int coverHeight) {
|
|||||||
}
|
}
|
||||||
coverRendered = false;
|
coverRendered = false;
|
||||||
requestUpdate();
|
requestUpdate();
|
||||||
} else if (StringUtils::checkFileExtension(book.path, ".xtch") ||
|
} else if (FsHelpers::hasXtcExtension(book.path)) {
|
||||||
StringUtils::checkFileExtension(book.path, ".xtc")) {
|
|
||||||
// Handle XTC file
|
// Handle XTC file
|
||||||
Xtc xtc(book.path, "/.crosspoint");
|
Xtc xtc(book.path, "/.crosspoint");
|
||||||
if (xtc.load()) {
|
if (xtc.load()) {
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
#include "RecentBooksStore.h"
|
#include "RecentBooksStore.h"
|
||||||
#include "components/UITheme.h"
|
#include "components/UITheme.h"
|
||||||
#include "fontIds.h"
|
#include "fontIds.h"
|
||||||
#include "util/StringUtils.h"
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr unsigned long GO_HOME_MS = 1000;
|
constexpr unsigned long GO_HOME_MS = 1000;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "ReaderActivity.h"
|
#include "ReaderActivity.h"
|
||||||
|
|
||||||
|
#include <FsHelpers.h>
|
||||||
#include <HalStorage.h>
|
#include <HalStorage.h>
|
||||||
|
|
||||||
#include "CrossPointSettings.h"
|
#include "CrossPointSettings.h"
|
||||||
@@ -11,7 +12,6 @@
|
|||||||
#include "XtcReaderActivity.h"
|
#include "XtcReaderActivity.h"
|
||||||
#include "activities/util/BmpViewerActivity.h"
|
#include "activities/util/BmpViewerActivity.h"
|
||||||
#include "activities/util/FullScreenMessageActivity.h"
|
#include "activities/util/FullScreenMessageActivity.h"
|
||||||
#include "util/StringUtils.h"
|
|
||||||
|
|
||||||
std::string ReaderActivity::extractFolderPath(const std::string& filePath) {
|
std::string ReaderActivity::extractFolderPath(const std::string& filePath) {
|
||||||
const auto lastSlash = filePath.find_last_of('/');
|
const auto lastSlash = filePath.find_last_of('/');
|
||||||
@@ -21,16 +21,14 @@ std::string ReaderActivity::extractFolderPath(const std::string& filePath) {
|
|||||||
return filePath.substr(0, lastSlash);
|
return filePath.substr(0, lastSlash);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ReaderActivity::isXtcFile(const std::string& path) {
|
bool ReaderActivity::isXtcFile(const std::string& path) { return FsHelpers::hasXtcExtension(path); }
|
||||||
return StringUtils::checkFileExtension(path, ".xtc") || StringUtils::checkFileExtension(path, ".xtch");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ReaderActivity::isTxtFile(const std::string& path) {
|
bool ReaderActivity::isTxtFile(const std::string& path) {
|
||||||
return StringUtils::checkFileExtension(path, ".txt") ||
|
return FsHelpers::hasTxtExtension(path) ||
|
||||||
StringUtils::checkFileExtension(path, ".md"); // Treat .md as txt files (until we have a markdown reader)
|
FsHelpers::hasMarkdownExtension(path); // Treat .md as txt files (until we have a markdown reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ReaderActivity::isBmpFile(const std::string& path) { return StringUtils::checkFileExtension(path, ".bmp"); }
|
bool ReaderActivity::isBmpFile(const std::string& path) { return FsHelpers::hasBmpExtension(path); }
|
||||||
|
|
||||||
std::unique_ptr<Epub> ReaderActivity::loadEpub(const std::string& path) {
|
std::unique_ptr<Epub> ReaderActivity::loadEpub(const std::string& path) {
|
||||||
if (!Storage.exists(path.c_str())) {
|
if (!Storage.exists(path.c_str())) {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "UITheme.h"
|
#include "UITheme.h"
|
||||||
|
|
||||||
|
#include <FsHelpers.h>
|
||||||
#include <GfxRenderer.h>
|
#include <GfxRenderer.h>
|
||||||
#include <Logging.h>
|
#include <Logging.h>
|
||||||
|
|
||||||
@@ -10,7 +11,6 @@
|
|||||||
#include "components/themes/BaseTheme.h"
|
#include "components/themes/BaseTheme.h"
|
||||||
#include "components/themes/lyra/Lyra3CoversTheme.h"
|
#include "components/themes/lyra/Lyra3CoversTheme.h"
|
||||||
#include "components/themes/lyra/LyraTheme.h"
|
#include "components/themes/lyra/LyraTheme.h"
|
||||||
#include "util/StringUtils.h"
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr int SKIP_PAGE_MS = 700;
|
constexpr int SKIP_PAGE_MS = 700;
|
||||||
@@ -74,18 +74,17 @@ std::string UITheme::getCoverThumbPath(std::string coverBmpPath, int coverHeight
|
|||||||
return coverBmpPath;
|
return coverBmpPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
UIIcon UITheme::getFileIcon(std::string filename) {
|
UIIcon UITheme::getFileIcon(const std::string& filename) {
|
||||||
if (filename.back() == '/') {
|
if (filename.back() == '/') {
|
||||||
return Folder;
|
return Folder;
|
||||||
}
|
}
|
||||||
if (StringUtils::checkFileExtension(filename, ".epub") || StringUtils::checkFileExtension(filename, ".xtch") ||
|
if (FsHelpers::hasEpubExtension(filename) || FsHelpers::hasXtcExtension(filename)) {
|
||||||
StringUtils::checkFileExtension(filename, ".xtc")) {
|
|
||||||
return Book;
|
return Book;
|
||||||
}
|
}
|
||||||
if (StringUtils::checkFileExtension(filename, ".txt") || StringUtils::checkFileExtension(filename, ".md")) {
|
if (FsHelpers::hasTxtExtension(filename) || FsHelpers::hasMarkdownExtension(filename)) {
|
||||||
return Text;
|
return Text;
|
||||||
}
|
}
|
||||||
if (StringUtils::checkFileExtension(filename, ".bmp")) {
|
if (FsHelpers::hasBmpExtension(filename)) {
|
||||||
return Image;
|
return Image;
|
||||||
}
|
}
|
||||||
return File;
|
return File;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class UITheme {
|
|||||||
static int getNumberOfItemsPerPage(const GfxRenderer& renderer, bool hasHeader, bool hasTabBar, bool hasButtonHints,
|
static int getNumberOfItemsPerPage(const GfxRenderer& renderer, bool hasHeader, bool hasTabBar, bool hasButtonHints,
|
||||||
bool hasSubtitle);
|
bool hasSubtitle);
|
||||||
static std::string getCoverThumbPath(std::string coverBmpPath, int coverHeight);
|
static std::string getCoverThumbPath(std::string coverBmpPath, int coverHeight);
|
||||||
static UIIcon getFileIcon(std::string filename);
|
static UIIcon getFileIcon(const std::string& filename);
|
||||||
static int getStatusBarHeight();
|
static int getStatusBarHeight();
|
||||||
static int getProgressBarHeight();
|
static int getProgressBarHeight();
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
#include "html/FilesPageHtml.generated.h"
|
#include "html/FilesPageHtml.generated.h"
|
||||||
#include "html/HomePageHtml.generated.h"
|
#include "html/HomePageHtml.generated.h"
|
||||||
#include "html/SettingsPageHtml.generated.h"
|
#include "html/SettingsPageHtml.generated.h"
|
||||||
#include "util/StringUtils.h"
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// Folders/files to hide from the web interface file browser
|
// Folders/files to hide from the web interface file browser
|
||||||
@@ -44,7 +43,7 @@ unsigned long wsLastCompleteAt = 0;
|
|||||||
// Helper function to clear epub cache after upload
|
// Helper function to clear epub cache after upload
|
||||||
void clearEpubCacheIfNeeded(const String& filePath) {
|
void clearEpubCacheIfNeeded(const String& filePath) {
|
||||||
// Only clear cache for .epub files
|
// Only clear cache for .epub files
|
||||||
if (StringUtils::checkFileExtension(filePath, ".epub")) {
|
if (FsHelpers::hasEpubExtension(filePath)) {
|
||||||
Epub(filePath.c_str(), "/.crosspoint").clearCache();
|
Epub(filePath.c_str(), "/.crosspoint").clearCache();
|
||||||
LOG_DBG("WEB", "Cleared epub cache for: %s", filePath.c_str());
|
LOG_DBG("WEB", "Cleared epub cache for: %s", filePath.c_str());
|
||||||
}
|
}
|
||||||
@@ -391,11 +390,7 @@ void CrossPointWebServer::scanFiles(const char* path, const std::function<void(F
|
|||||||
root.close();
|
root.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CrossPointWebServer::isEpubFile(const String& filename) const {
|
bool CrossPointWebServer::isEpubFile(const String& filename) const { return FsHelpers::hasEpubExtension(filename); }
|
||||||
String lower = filename;
|
|
||||||
lower.toLowerCase();
|
|
||||||
return lower.endsWith(".epub");
|
|
||||||
}
|
|
||||||
|
|
||||||
void CrossPointWebServer::handleFileList() const {
|
void CrossPointWebServer::handleFileList() const {
|
||||||
sendHtmlContent(server.get(), FilesPageHtml, sizeof(FilesPageHtml));
|
sendHtmlContent(server.get(), FilesPageHtml, sizeof(FilesPageHtml));
|
||||||
|
|||||||
@@ -6,8 +6,6 @@
|
|||||||
#include <Logging.h>
|
#include <Logging.h>
|
||||||
#include <esp_task_wdt.h>
|
#include <esp_task_wdt.h>
|
||||||
|
|
||||||
#include "util/StringUtils.h"
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
const char* HIDDEN_ITEMS[] = {"System Volume Information", "XTCache"};
|
const char* HIDDEN_ITEMS[] = {"System Volume Information", "XTCache"};
|
||||||
constexpr size_t HIDDEN_ITEMS_COUNT = sizeof(HIDDEN_ITEMS) / sizeof(HIDDEN_ITEMS[0]);
|
constexpr size_t HIDDEN_ITEMS_COUNT = sizeof(HIDDEN_ITEMS) / sizeof(HIDDEN_ITEMS[0]);
|
||||||
@@ -801,28 +799,26 @@ bool WebDAVHandler::getOverwrite(WebServer& s) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WebDAVHandler::clearEpubCacheIfNeeded(const String& path) const {
|
void WebDAVHandler::clearEpubCacheIfNeeded(const String& path) const {
|
||||||
if (StringUtils::checkFileExtension(path, ".epub")) {
|
if (FsHelpers::hasEpubExtension(path)) {
|
||||||
Epub(path.c_str(), "/.crosspoint").clearCache();
|
Epub(path.c_str(), "/.crosspoint").clearCache();
|
||||||
LOG_DBG("DAV", "Cleared epub cache for: %s", path.c_str());
|
LOG_DBG("DAV", "Cleared epub cache for: %s", path.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String WebDAVHandler::getMimeType(const String& path) const {
|
String WebDAVHandler::getMimeType(const String& path) const {
|
||||||
if (StringUtils::checkFileExtension(path, ".epub")) return "application/epub+zip";
|
if (FsHelpers::hasEpubExtension(path)) return "application/epub+zip";
|
||||||
if (StringUtils::checkFileExtension(path, ".pdf")) return "application/pdf";
|
if (FsHelpers::checkFileExtension(path, ".pdf")) return "application/pdf";
|
||||||
if (StringUtils::checkFileExtension(path, ".txt")) return "text/plain";
|
if (FsHelpers::hasTxtExtension(path)) return "text/plain";
|
||||||
if (StringUtils::checkFileExtension(path, ".html") || StringUtils::checkFileExtension(path, ".htm"))
|
if (FsHelpers::checkFileExtension(path, ".html") || FsHelpers::checkFileExtension(path, ".htm")) return "text/html";
|
||||||
return "text/html";
|
if (FsHelpers::checkFileExtension(path, ".css")) return "text/css";
|
||||||
if (StringUtils::checkFileExtension(path, ".css")) return "text/css";
|
if (FsHelpers::checkFileExtension(path, ".js")) return "application/javascript";
|
||||||
if (StringUtils::checkFileExtension(path, ".js")) return "application/javascript";
|
if (FsHelpers::checkFileExtension(path, ".json")) return "application/json";
|
||||||
if (StringUtils::checkFileExtension(path, ".json")) return "application/json";
|
if (FsHelpers::checkFileExtension(path, ".xml")) return "application/xml";
|
||||||
if (StringUtils::checkFileExtension(path, ".xml")) return "application/xml";
|
if (FsHelpers::hasJpgExtension(path)) return "image/jpeg";
|
||||||
if (StringUtils::checkFileExtension(path, ".jpg") || StringUtils::checkFileExtension(path, ".jpeg"))
|
if (FsHelpers::hasPngExtension(path)) return "image/png";
|
||||||
return "image/jpeg";
|
if (FsHelpers::hasGifExtension(path)) return "image/gif";
|
||||||
if (StringUtils::checkFileExtension(path, ".png")) return "image/png";
|
if (FsHelpers::checkFileExtension(path, ".svg")) return "image/svg+xml";
|
||||||
if (StringUtils::checkFileExtension(path, ".gif")) return "image/gif";
|
if (FsHelpers::checkFileExtension(path, ".zip")) return "application/zip";
|
||||||
if (StringUtils::checkFileExtension(path, ".svg")) return "image/svg+xml";
|
if (FsHelpers::checkFileExtension(path, ".gz")) return "application/gzip";
|
||||||
if (StringUtils::checkFileExtension(path, ".zip")) return "application/zip";
|
|
||||||
if (StringUtils::checkFileExtension(path, ".gz")) return "application/gzip";
|
|
||||||
return "application/octet-stream";
|
return "application/octet-stream";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
#include <Utf8.h>
|
#include <Utf8.h>
|
||||||
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
namespace StringUtils {
|
namespace StringUtils {
|
||||||
|
|
||||||
std::string sanitizeFilename(const std::string& name, size_t maxBytes) {
|
std::string sanitizeFilename(const std::string& name, size_t maxBytes) {
|
||||||
@@ -45,30 +43,4 @@ std::string sanitizeFilename(const std::string& name, size_t maxBytes) {
|
|||||||
return result.empty() ? "book" : result;
|
return result.empty() ? "book" : result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool checkFileExtension(const std::string& fileName, const char* extension) {
|
|
||||||
if (fileName.length() < strlen(extension)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string fileExt = fileName.substr(fileName.length() - strlen(extension));
|
|
||||||
for (size_t i = 0; i < fileExt.length(); i++) {
|
|
||||||
if (tolower(fileExt[i]) != tolower(extension[i])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool checkFileExtension(const String& fileName, const char* extension) {
|
|
||||||
if (fileName.length() < strlen(extension)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
String localFile(fileName);
|
|
||||||
String localExtension(extension);
|
|
||||||
localFile.toLowerCase();
|
|
||||||
localExtension.toLowerCase();
|
|
||||||
return localFile.endsWith(localExtension);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace StringUtils
|
} // namespace StringUtils
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <WString.h>
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace StringUtils {
|
namespace StringUtils {
|
||||||
@@ -13,10 +11,4 @@ namespace StringUtils {
|
|||||||
*/
|
*/
|
||||||
std::string sanitizeFilename(const std::string& name, size_t maxBytes = 100);
|
std::string sanitizeFilename(const std::string& name, size_t maxBytes = 100);
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the given filename ends with the specified extension (case-insensitive).
|
|
||||||
*/
|
|
||||||
bool checkFileExtension(const std::string& fileName, const char* extension);
|
|
||||||
bool checkFileExtension(const String& fileName, const char* extension);
|
|
||||||
|
|
||||||
} // namespace StringUtils
|
} // namespace StringUtils
|
||||||
|
|||||||
Reference in New Issue
Block a user