Skip to content

Commit

Permalink
some work to "ease up" on complexity of file io and hardening, buuuut…
Browse files Browse the repository at this point in the history
… it's not super pretty yet.
  • Loading branch information
coornio committed Sep 26, 2024
1 parent da7b25d commit 9fd8032
Show file tree
Hide file tree
Showing 8 changed files with 269 additions and 108 deletions.
1 change: 1 addition & 0 deletions CubeChip (SDL).vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
<ClInclude Include="src\Assistants\PathGetters.hpp" />
<ClInclude Include="src\Assistants\RangeIterator.hpp" />
<ClInclude Include="src\Assistants\SHA1.hpp" />
<ClInclude Include="src\Assistants\SimpleFileIO.hpp" />
<ClInclude Include="src\Assistants\Typedefs.hpp" />
<ClInclude Include="src\Assistants\Well512.hpp" />
<ClInclude Include="src\EmuHost.hpp" />
Expand Down
3 changes: 3 additions & 0 deletions CubeChip (SDL).vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -196,5 +196,8 @@
<ClInclude Include="src\Systems\GAMEBOY\Cores\GAMEBOY_CLASSIC.hpp">
<Filter>Source Files\Systems\GAMEBOY\Cores</Filter>
</ClInclude>
<ClInclude Include="src\Assistants\SimpleFileIO.hpp">
<Filter>Source Files\Assistants</Filter>
</ClInclude>
</ItemGroup>
</Project>
57 changes: 18 additions & 39 deletions src/Assistants/HomeDirManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,12 @@
#include "HomeDirManager.hpp"

#include "../Assistants/SHA1.hpp"
#include "../Assistants/SimpleFileIO.hpp"
#include "../Assistants/PathGetters.hpp"
#include "../Assistants/BasicLogger.hpp"

#include <SDL3/SDL_messagebox.h>

/*==================================================================*/

[[maybe_unused]]
static auto getFileModTime(const Path& filePath) noexcept {
std::error_code error;
return std::filesystem::last_write_time(filePath, error);
}

[[maybe_unused]]
static auto getFileSize(const Path& filePath) noexcept {
std::error_code error;
return std::filesystem::file_size(filePath, error);
}

/*==================================================================*/
#pragma region HomeDirManager Class

Expand Down Expand Up @@ -82,52 +69,44 @@ void HomeDirManager::clearCachedFileData() noexcept {
}

bool HomeDirManager::validateGameFile(const Path gamePath) noexcept {
if (gamePath.empty()) { return false; }
namespace fs = std::filesystem;
std::error_code error;

blog.newEntry(BLOG::INFO, "Attempting to access file: {}", gamePath.string());

if (!fs::exists(gamePath, error) || error) {
blog.newEntry(BLOG::WARN, "Unable to locate path! {}", error.message());
if (!::doesFileExist(gamePath, &error) || error) {
blog.newEntry(BLOG::WARN, "Path is ineligible: {}", error.message());
return false;
}

if (!fs::is_regular_file(gamePath, error) || error) {
blog.newEntry(BLOG::WARN, "Provided path is not to a file!");
const auto fileSize{ ::getFileSize(gamePath, &error) };
if (error) {
blog.newEntry(BLOG::WARN, "Path is ineligible: {}", error.message());
return false;
}

const auto tempTime{ getFileModTime(gamePath) };

std::ifstream ifs(gamePath, std::ios::binary);
mFileData.assign(std::istreambuf_iterator(ifs), {});
if (fileSize == 0) {
blog.newEntry(BLOG::WARN, "Game file must not be empty!");
return false;
}

if (tempTime != getFileModTime(gamePath)) {
blog.newEntry(BLOG::WARN, "File was modified while reading!");
if (fileSize >= 33'554'432) { // 32 MB upper limit
blog.newEntry(BLOG::WARN, "Game file is too large!");
return false;
}

if (!getFileSize()) {
blog.newEntry(BLOG::WARN, "File must not be empty!");
mFileData = std::move(::readFileData(gamePath, &error));
if (error) {
blog.newEntry(BLOG::WARN, "Path is ineligible: {}", error.message());
return false;
}

const auto tempSHA1{ SHA1::from_span(mFileData) };
const bool gameApproved{ checkGame(mFileData, gamePath.extension().string(), tempSHA1)};

if (gameApproved) {
if (checkGame(mFileData, gamePath.extension().string(), tempSHA1)) {
mFilePath = gamePath;
mFileSHA1 = tempSHA1;
}

if (gameApproved) {
blog.newEntry(BLOG::INFO, "File is a valid game!");
return true;
} else {
blog.newEntry(BLOG::INFO, "File is not a valid game!");
return false;
}

return gameApproved;
}

#pragma endregion
Expand Down
8 changes: 4 additions & 4 deletions src/Assistants/HomeDirManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ class HomeDirManager final {

using GameValidator = bool (*)(
std::span<const char>,
const std::string&,
const std::string&
const Str&,
const Str&
) noexcept;

Path mFilePath{};
std::string mFileSHA1{};
Path mFilePath{};
Str mFileSHA1{};

std::vector<char>
mFileData{};
Expand Down
150 changes: 150 additions & 0 deletions src/Assistants/SimpleFileIO.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
/*
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

#pragma once

#include <span>
#include <vector>
#include <fstream>
#include <concepts>
#include <filesystem>

#include "Typedefs.hpp"

/*==================================================================*/

[[maybe_unused]]
inline auto getFileModTime(
const Path& filePath,
std::error_code* const ioError = nullptr
) noexcept {
std::error_code error;
const auto modifiedTime{ std::filesystem::last_write_time(filePath, error) };
if (error && ioError) { *ioError = error; }
return modifiedTime;
}

[[maybe_unused]]
inline auto getFileSize(
const Path& filePath,
std::error_code* const ioError = nullptr
) noexcept {
std::error_code error;
const auto fileSize{ std::filesystem::file_size(filePath, error) };
if (error && ioError) { *ioError = error; }
return fileSize;
}

[[maybe_unused]]
inline auto doesPathExist(
const Path& filePath,
std::error_code* const ioError = nullptr
) noexcept {
std::error_code error;

// Check if the path doesn't lead anywhere
if (!std::filesystem::exists(filePath, error) || error) {
if (ioError) { *ioError = error; }
return false;
} else {
return true;
}
}

[[maybe_unused]]
inline auto doesFileExist(
const Path& filePath,
std::error_code* const ioError = nullptr
) noexcept {
std::error_code error;

// Check if the path doesn't lead to a regular file
if (!std::filesystem::is_regular_file(filePath, error) || error) {
if (ioError) { *ioError = error; }
return false;
} else {
return true;
}
}

[[maybe_unused]]
inline auto readFileData(
const Path& filePath,
std::error_code* const ioError = nullptr
) noexcept {
std::vector<char> fileData{};
std::error_code error;

// Attempt first file mod time fetch
const auto fileModStampBegin{ ::getFileModTime(filePath, &error) };
if (error) {
if (ioError) { *ioError = error; }
fileData.clear(); return fileData;
}

std::ifstream ifs(filePath, std::ios::binary);

// Attempt to init file stream
if (ifs) {
try {
// Attempt to read data into vector
fileData.assign(std::istreambuf_iterator(ifs), {});
} catch (const std::exception&) {
if (ioError) { *ioError = std::make_error_code(std::errc::not_enough_memory); }
fileData.clear(); return fileData;
}
// Check if the stream failed to reach EOF safely
if (!ifs.good()) {
if (ioError) { *ioError = std::make_error_code(std::errc::io_error); }
fileData.clear(); return fileData;
}
} else {
if (ioError) {
*ioError = std::make_error_code(std::errc::permission_denied);
fileData.clear(); return fileData;
}
}

// Attempt second file mod time fetch
const auto fileModStampEnd{ ::getFileModTime(filePath, &error) };
if (error) {
if (ioError) { *ioError = error; }
fileData.clear(); return fileData;
}

// Compare times to ensure no modification occurred during read
if (fileModStampBegin != fileModStampEnd) {
if (ioError) { *ioError = std::make_error_code(std::errc::interrupted); }
fileData.clear(); return fileData;
} else {
return fileData;
}
}

template <typename T> [[maybe_unused]]
inline auto writeFileData(
const Path& filePath,
std::span<const T> fileData,
std::error_code* const ioError = nullptr
) noexcept requires std::is_trivially_constructible_v<T> {
std::ofstream ofs(filePath, std::ios::binary);

// Attempt to init file stream
if (ofs) {
ofs.write(reinterpret_cast<const char*>(fileData.data()), fileData.size() * sizeof(T));

// Check if the stream failed to write fully
if (!ofs.good()) {
if (ioError) { *ioError = std::make_error_code(std::errc::io_error); }
return true;
} else {
return false;
}
} else {
if (ioError) { *ioError = std::make_error_code(std::errc::permission_denied); }
return true;
}
}
4 changes: 4 additions & 0 deletions src/EmuHost.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,12 @@ void EmuHost::replaceCore() {

void EmuHost::loadGameFile(const Path& gameFile) {
BVS->raiseWindow();
blog.newEntry(BLOG::INFO, "Attempting to access file: \"{}\"", gameFile.string());
if (HDM->validateGameFile(gameFile)) {
replaceCore();
blog.newEntry(BLOG::INFO, "File has been accepted!");
} else {
blog.newEntry(BLOG::INFO, "File has been rejected!");
}
}

Expand Down
Loading

0 comments on commit 9fd8032

Please sign in to comment.