## Summary I love the new screenshot feature but I can tell that soon I'm gonna have a folder full of screenshots and not know what's what. It would be great if the folder could self organize. When a screenshot is taken while reading, the filename would now include the book title, chapter (EPUB), page number, and progress percentage. Example: My-Great-Book_ch3_p5_42pct_12345.bmp * **What is the goal of this PR?** (e.g., Implements the new feature for file uploading.) * **What changes are included?** ## Additional Context * Non-reader screens keep the existing screenshot-<millis>.bmp naming. --- ### 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? _**< YES >**_ I reviewed all the changes but I don't have any experience with this project so this is my best guess
108 lines
2.8 KiB
C++
108 lines
2.8 KiB
C++
#include "FsHelpers.h"
|
|
|
|
#include <cctype>
|
|
#include <cstring>
|
|
#include <vector>
|
|
|
|
namespace FsHelpers {
|
|
|
|
std::string normalisePath(const std::string& path) {
|
|
std::vector<std::string> components;
|
|
std::string component;
|
|
|
|
for (const auto c : path) {
|
|
if (c == '/') {
|
|
if (!component.empty()) {
|
|
if (component == "..") {
|
|
if (!components.empty()) {
|
|
components.pop_back();
|
|
}
|
|
} else {
|
|
components.push_back(component);
|
|
}
|
|
component.clear();
|
|
}
|
|
} else {
|
|
component += c;
|
|
}
|
|
}
|
|
|
|
if (!component.empty()) {
|
|
components.push_back(component);
|
|
}
|
|
|
|
std::string result;
|
|
for (const auto& c : components) {
|
|
if (!result.empty()) {
|
|
result += "/";
|
|
}
|
|
result += c;
|
|
}
|
|
|
|
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"); }
|
|
|
|
std::string extractFolderPath(const std::string& filePath) {
|
|
const auto lastSlash = filePath.find_last_of('/');
|
|
if (lastSlash == std::string::npos || lastSlash == 0) {
|
|
return "/";
|
|
}
|
|
return filePath.substr(0, lastSlash);
|
|
}
|
|
|
|
void sanitizePathComponentForFat32(const char* input, char* output, size_t maxLen) {
|
|
if (maxLen == 0) {
|
|
return;
|
|
}
|
|
|
|
size_t i = 0;
|
|
for (; i < maxLen - 1 && input[i] != '\0'; i++) {
|
|
const char c = input[i];
|
|
if (c == '\\' || c == '/' || c == ':' || c == '*' || c == '?' || c == '"' || c == '<' || c == '>' || c == '|' ||
|
|
c == ' ' || (c > 0x00 && c <= 0x1f)) {
|
|
output[i] = '-';
|
|
} else {
|
|
output[i] = c;
|
|
}
|
|
}
|
|
output[i] = '\0';
|
|
}
|
|
|
|
} // namespace FsHelpers
|