From d7750a702c77cc7590d6bdfda5557b38f554382f Mon Sep 17 00:00:00 2001 From: Chris Blume Date: Fri, 27 Jun 2025 07:39:05 -0400 Subject: [PATCH] Use monadic operations --- Code/png_cicp_editor/Actions.cpp | 64 +-- Code/png_cicp_editor/CICPInserter.cpp | 266 +++++------ Code/png_cicp_editor/CICPInserter.hpp | 52 +- Code/png_cicp_editor/EntryPoint.cpp | 26 +- Code/png_cicp_editor/Error.hpp | 77 +-- Code/png_cicp_editor/FileReader.cpp | 84 ++-- Code/png_cicp_editor/FileReader.hpp | 52 +- Code/png_cicp_editor/FileWriter.cpp | 86 ++-- Code/png_cicp_editor/FileWriter.hpp | 54 +-- Code/png_cicp_editor/PNGParser.cpp | 114 ++--- Code/png_cicp_editor/PNGParser.hpp | 50 +- Projects/VisualStudio/png_cicp_editor.vcxproj | 448 +++++++++--------- .../png_cicp_editor.vcxproj.filters | 258 +++++----- 13 files changed, 815 insertions(+), 816 deletions(-) diff --git a/Code/png_cicp_editor/Actions.cpp b/Code/png_cicp_editor/Actions.cpp index b4de20e..c532a62 100644 --- a/Code/png_cicp_editor/Actions.cpp +++ b/Code/png_cicp_editor/Actions.cpp @@ -150,37 +150,27 @@ png_cicp_editor depends on max: print_bsd_3_clause_license(max_year, max_copyright_holder); } - - void ActionExecutor::operator()(const AddAction& action) const noexcept { // TODO: Return error values - // Read the file - auto file_contents = read_file(action.file_path_); + + auto file_contents = read_file(action.file_path_) + .transform_error(print_monad_error); if (!file_contents.has_value()) { - print_error(file_contents.error()); - //return 1; return; } - - // Get indicies of all chunks - auto chunk_indices = get_chunk_indices(file_contents.value()); + auto chunk_indices = get_chunk_indices(file_contents.value()) + .transform_error(print_monad_error); if (!chunk_indices.has_value()) { - print_error(chunk_indices.error()); - //return 1; return; } - - // Find overwrite index for cICP chunk - auto split_buffer = get_split_buffer_across_cicp_insertion_point(file_contents.value(), chunk_indices.value(), /*overwrite_cicp*/ false); + auto split_buffer = get_split_buffer_across_cicp_insertion_point(file_contents.value(), chunk_indices.value(), /*overwrite_cicp*/ false) + .transform_error(print_monad_error); if (!split_buffer.has_value()) { - print_error(split_buffer.error()); - //return 1; return; } - // Prepare cICP buffer to write auto cicp_buffer = create_cicp_buffer(action.cicp_); @@ -193,10 +183,9 @@ png_cicp_editor depends on max: // Write the file with cICP inserted - auto write_result = PNG_CICP_Editor::write_file(action.file_path_, buffers); + auto write_result = PNG_CICP_Editor::write_file(action.file_path_, buffers) + .transform_error(print_monad_error); if (!write_result.has_value()) { - print_error(write_result.error()); - //return 1; return; } } @@ -204,28 +193,25 @@ png_cicp_editor depends on max: void ActionExecutor::operator()(const OverwriteAction& action) const noexcept { // TODO: Return error values // Read the file - auto file_contents = read_file(action.file_path_); + auto file_contents = read_file(action.file_path_) + .transform_error(print_monad_error); if (!file_contents.has_value()) { - print_error(file_contents.error()); - //return 1; return; } // Get indicies of all chunks - auto chunk_indices = get_chunk_indices(file_contents.value()); + auto chunk_indices = get_chunk_indices(file_contents.value()) + .transform_error(print_monad_error); if (!chunk_indices.has_value()) { - print_error(chunk_indices.error()); - //return 1; return; } // Find overwrite index for cICP chunk - auto split_buffer = get_split_buffer_across_cicp_insertion_point(file_contents.value(), chunk_indices.value(), /*overwrite_cicp*/ true); + auto split_buffer = get_split_buffer_across_cicp_insertion_point(file_contents.value(), chunk_indices.value(), /*overwrite_cicp*/ true) + .transform_error(print_monad_error); if (!split_buffer.has_value()) { - print_error(split_buffer.error()); - //return 1; return; } @@ -242,10 +228,9 @@ png_cicp_editor depends on max: // Write the file with cICP inserted - auto write_result = PNG_CICP_Editor::write_file(action.file_path_, buffers); + auto write_result = PNG_CICP_Editor::write_file(action.file_path_, buffers) + .transform_error(print_monad_error); if (!write_result.has_value()) { - print_error(write_result.error()); - //return 1; return; } } @@ -253,19 +238,17 @@ png_cicp_editor depends on max: void ActionExecutor::operator()(const RemoveAction& action) const noexcept { // TODO: Return error values // Read the file - auto file_contents = read_file(action.file_path_); + auto file_contents = read_file(action.file_path_) + .transform_error(print_monad_error); if (!file_contents.has_value()) { - print_error(file_contents.error()); - //return 1; return; } // Get indicies of all chunks - auto chunk_indices = get_chunk_indices(file_contents.value()); + auto chunk_indices = get_chunk_indices(file_contents.value()) + .transform_error(print_monad_error); if (!chunk_indices.has_value()) { - print_error(chunk_indices.error()); - //return 1; return; } @@ -310,10 +293,9 @@ png_cicp_editor depends on max: // Write the file with cICP inserted - auto write_result = PNG_CICP_Editor::write_file(action.file_path_, buffers); + auto write_result = PNG_CICP_Editor::write_file(action.file_path_, buffers) + .transform_error(print_monad_error); if (!write_result.has_value()) { - print_error(write_result.error()); - //return 1; return; } } diff --git a/Code/png_cicp_editor/CICPInserter.cpp b/Code/png_cicp_editor/CICPInserter.cpp index de9a588..cf3efe4 100644 --- a/Code/png_cicp_editor/CICPInserter.cpp +++ b/Code/png_cicp_editor/CICPInserter.cpp @@ -1,133 +1,133 @@ -// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "CICPInserter.hpp" - -#include -#include -#include -#include -#include - -namespace { - - // error messages - static constinit std::string_view cicp_chunk_already_exists = "cICP chunk already exists in file."; - static constinit std::string_view could_not_find_insertion_point = "Could not find insertion point."; - static constinit std::string_view cicp_chunk_in_invalid_position = "cICP chunk being overwritten is in an invalid location"; - - // utility - static constinit std::string_view newline = "\n"; - - - constexpr std::array get_chunk_type(const std::span& file_contents, size_t index) noexcept { - return { - file_contents[index + 4], - file_contents[index + 5], - file_contents[index + 6], - file_contents[index + 7] - }; - } - -} // anonymous namespace - -namespace PNG_CICP_Editor { - - std::expected>, GetInsertionIndexError> get_split_buffer_across_cicp_insertion_point(const std::span& file_contents, const std::vector& chunk_indices, bool overwrite_cicp) noexcept { - auto plte_index = size_t{ SIZE_MAX }; - auto idat_index = size_t{ SIZE_MAX }; - auto cicp_index = size_t{ SIZE_MAX }; - for (auto& index : chunk_indices) { - auto chunk_type = get_chunk_type(file_contents, index); - - // Use first PLTE index if there are multiple. - // There shouldn't be for PLTE but our goal isn't to identify invalid PNGs. - if (plte_index == SIZE_MAX && - chunk_type[0] == 'P' && - chunk_type[1] == 'L' && - chunk_type[2] == 'T' && - chunk_type[3] == 'E') { - plte_index = index; - } - // Use first IDAT index if there are multiple. - else if (idat_index == SIZE_MAX && - chunk_type[0] == 'I' && - chunk_type[1] == 'D' && - chunk_type[2] == 'A' && - chunk_type[3] == 'T') { - idat_index = index; - } - else if (chunk_type[0] == 'c' && - chunk_type[1] == 'I' && - chunk_type[2] == 'C' && - chunk_type[3] == 'P') { - if (!overwrite_cicp) { - return std::unexpected{ GetInsertionIndexError{ GetInsertionIndexErrorCode::CICPChunkAlreadyExists, { cicp_chunk_already_exists, newline } } }; - } - // TODO: Handle multiple cICP chunks - cicp_index = index; - } - } - - if (plte_index == SIZE_MAX && idat_index == SIZE_MAX) { - return std::unexpected{ GetInsertionIndexError{ GetInsertionIndexErrorCode::CouldNotFindInsertionPoint, { could_not_find_insertion_point, newline } } }; - } - - - // There are several combinations of potential splits. - // - // A PNG might look like {IHDR, PLTE, IDAT, IEND}. - // In this case, we can split as {IHDR}, {PLTE, IDAT, IEND}. - // - // After the cICP chunk is inserted, picking the first of (PLTE/IDAT) - // and overwriting the cICP results in the same split: - // {IHDR}, {PLTE, IDAT, IEND} - // - // However, what if the PNG looked like {IHDR, PLTE, cICP, IDAT, IEND}. - // If we split in front of the first (PLTE/IDAT) & remove the cICP, we'll see these splits: - // {IHDR}, {PLTE}, {IDAT, IEND} - // - // Alternatively, we could simply overwrite the cICP in-place, falling back to first of - // (PLTE/IDAT) if no cICP is found. Then that last PNG's splits would look like: - // {IHDR, PLTE}, {IDAT, IEND} - // - // This gives us a consistent before & after scenario. - - std::vector> buffers; - auto file_contents_start = file_contents.data(); - - if (cicp_index != SIZE_MAX) { - // A cICP chunk was found. Split before and after this chunk - buffers.push_back({ file_contents_start, file_contents_start + cicp_index }); - - // find the chunk after cICP - auto index_after_cicp = size_t{ 0 }; - for (size_t i = 0; i < chunk_indices.size(); i++) { - if (chunk_indices[i] == cicp_index) { - if (i + 1 == chunk_indices.size()) { - // there is no next chunk - return std::unexpected{ GetInsertionIndexError{ GetInsertionIndexErrorCode::CouldNotFindInsertionPoint, { cicp_chunk_in_invalid_position, newline } } }; - } - index_after_cicp = chunk_indices[i + 1]; - break; - } - } - - buffers.push_back({ file_contents_start + index_after_cicp, file_contents.size() - index_after_cicp }); - } - else { - // A cICP chunk was not found - - // The cICP chunk must come before the PLTE and IDAT chunks. - // It could come anywhere before that (but after IHDR). - // Inserting it just before the first chunk it must proceed is fine. - auto split_index = std::min(plte_index, idat_index); - buffers.push_back({ file_contents_start, file_contents_start + split_index }); - buffers.push_back({ file_contents_start + split_index, file_contents.size() - split_index }); - } - - return buffers; - } - -} // namespace PNG_CICP_Editor +// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "CICPInserter.hpp" + +#include +#include +#include +#include +#include + +namespace { + + // error messages + static constinit std::string_view cicp_chunk_already_exists = "cICP chunk already exists in file."; + static constinit std::string_view could_not_find_insertion_point = "Could not find insertion point."; + static constinit std::string_view cicp_chunk_in_invalid_position = "cICP chunk being overwritten is in an invalid location"; + + // utility + static constinit std::string_view newline = "\n"; + + + constexpr std::array get_chunk_type(const std::span& file_contents, size_t index) noexcept { + return { + file_contents[index + 4], + file_contents[index + 5], + file_contents[index + 6], + file_contents[index + 7] + }; + } + +} // anonymous namespace + +namespace PNG_CICP_Editor { + + std::expected>, GetInsertionIndexError> get_split_buffer_across_cicp_insertion_point(const std::span& file_contents, const std::vector& chunk_indices, bool overwrite_cicp) noexcept { + auto plte_index = size_t{ SIZE_MAX }; + auto idat_index = size_t{ SIZE_MAX }; + auto cicp_index = size_t{ SIZE_MAX }; + for (auto& index : chunk_indices) { + auto chunk_type = get_chunk_type(file_contents, index); + + // Use first PLTE index if there are multiple. + // There shouldn't be for PLTE but our goal isn't to identify invalid PNGs. + if (plte_index == SIZE_MAX && + chunk_type[0] == 'P' && + chunk_type[1] == 'L' && + chunk_type[2] == 'T' && + chunk_type[3] == 'E') { + plte_index = index; + } + // Use first IDAT index if there are multiple. + else if (idat_index == SIZE_MAX && + chunk_type[0] == 'I' && + chunk_type[1] == 'D' && + chunk_type[2] == 'A' && + chunk_type[3] == 'T') { + idat_index = index; + } + else if (chunk_type[0] == 'c' && + chunk_type[1] == 'I' && + chunk_type[2] == 'C' && + chunk_type[3] == 'P') { + if (!overwrite_cicp) { + return std::unexpected{ GetInsertionIndexError{ GetInsertionIndexErrorCode::CICPChunkAlreadyExists, { cicp_chunk_already_exists, newline } } }; + } + // TODO: Handle multiple cICP chunks + cicp_index = index; + } + } + + if (plte_index == SIZE_MAX && idat_index == SIZE_MAX) { + return std::unexpected{ GetInsertionIndexError{ GetInsertionIndexErrorCode::CouldNotFindInsertionPoint, { could_not_find_insertion_point, newline } } }; + } + + + // There are several combinations of potential splits. + // + // A PNG might look like {IHDR, PLTE, IDAT, IEND}. + // In this case, we can split as {IHDR}, {PLTE, IDAT, IEND}. + // + // After the cICP chunk is inserted, picking the first of (PLTE/IDAT) + // and overwriting the cICP results in the same split: + // {IHDR}, {PLTE, IDAT, IEND} + // + // However, what if the PNG looked like {IHDR, PLTE, cICP, IDAT, IEND}. + // If we split in front of the first (PLTE/IDAT) & remove the cICP, we'll see these splits: + // {IHDR}, {PLTE}, {IDAT, IEND} + // + // Alternatively, we could simply overwrite the cICP in-place, falling back to first of + // (PLTE/IDAT) if no cICP is found. Then that last PNG's splits would look like: + // {IHDR, PLTE}, {IDAT, IEND} + // + // This gives us a consistent before & after scenario. + + std::vector> buffers; + auto file_contents_start = file_contents.data(); + + if (cicp_index != SIZE_MAX) { + // A cICP chunk was found. Split before and after this chunk + buffers.push_back({ file_contents_start, file_contents_start + cicp_index }); + + // find the chunk after cICP + auto index_after_cicp = size_t{ 0 }; + for (size_t i = 0; i < chunk_indices.size(); i++) { + if (chunk_indices[i] == cicp_index) { + if (i + 1 == chunk_indices.size()) { + // there is no next chunk + return std::unexpected{ GetInsertionIndexError{ GetInsertionIndexErrorCode::CouldNotFindInsertionPoint, { cicp_chunk_in_invalid_position, newline } } }; + } + index_after_cicp = chunk_indices[i + 1]; + break; + } + } + + buffers.push_back({ file_contents_start + index_after_cicp, file_contents.size() - index_after_cicp }); + } + else { + // A cICP chunk was not found + + // The cICP chunk must come before the PLTE and IDAT chunks. + // It could come anywhere before that (but after IHDR). + // Inserting it just before the first chunk it must proceed is fine. + auto split_index = std::min(plte_index, idat_index); + buffers.push_back({ file_contents_start, file_contents_start + split_index }); + buffers.push_back({ file_contents_start + split_index, file_contents.size() - split_index }); + } + + return buffers; + } + +} // namespace PNG_CICP_Editor diff --git a/Code/png_cicp_editor/CICPInserter.hpp b/Code/png_cicp_editor/CICPInserter.hpp index f46aaca..591af45 100644 --- a/Code/png_cicp_editor/CICPInserter.hpp +++ b/Code/png_cicp_editor/CICPInserter.hpp @@ -1,26 +1,26 @@ -// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef PNG_CICP_EDITOR_CICPINSERTER_HPP -#define PNG_CICP_EDITOR_CICPINSERTER_HPP - -#include -#include -#include - -#include "Error.hpp" - -namespace PNG_CICP_Editor { - - enum class GetInsertionIndexErrorCode { - CICPChunkAlreadyExists, - CouldNotFindInsertionPoint, - }; - using GetInsertionIndexError = ErrorWithCode; - - std::expected>, GetInsertionIndexError> get_split_buffer_across_cicp_insertion_point(const std::span& file_contents, const std::vector& chunk_indices, bool overwrite_cicp) noexcept; - -} // namespace PNG_CICP_Editor - -#endif // #ifndef PNG_CICP_EDITOR_CICPINSERTER_HPP +// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PNG_CICP_EDITOR_CICPINSERTER_HPP +#define PNG_CICP_EDITOR_CICPINSERTER_HPP + +#include +#include +#include + +#include "Error.hpp" + +namespace PNG_CICP_Editor { + + enum class GetInsertionIndexErrorCode { + CICPChunkAlreadyExists, + CouldNotFindInsertionPoint, + }; + using GetInsertionIndexError = ErrorWithCode; + + std::expected>, GetInsertionIndexError> get_split_buffer_across_cicp_insertion_point(const std::span& file_contents, const std::vector& chunk_indices, bool overwrite_cicp) noexcept; + +} // namespace PNG_CICP_Editor + +#endif // #ifndef PNG_CICP_EDITOR_CICPINSERTER_HPP diff --git a/Code/png_cicp_editor/EntryPoint.cpp b/Code/png_cicp_editor/EntryPoint.cpp index 02c94b1..6873890 100644 --- a/Code/png_cicp_editor/EntryPoint.cpp +++ b/Code/png_cicp_editor/EntryPoint.cpp @@ -2,19 +2,29 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include +#include + #include "Actions.hpp" #include "CommandLineParameters.hpp" #include "Error.hpp" -int main(int argc, char const* argv[]) noexcept { - // Parse the command line parameters - auto command_line_parameters = PNG_CICP_Editor::parse_command_line_parameters(argc, argv); - if (!command_line_parameters.has_value()) { - print_error(command_line_parameters.error()); - return 1; +namespace { + + using parsed_command_line_type = std::invoke_result_t; + + parsed_command_line_type execute_action(PNG_CICP_Editor::Action action) noexcept { + std::visit(PNG_CICP_Editor::ActionExecutor(), action); + return action; } - std::visit(PNG_CICP_Editor::ActionExecutor(), *command_line_parameters); +} // anonymous namespace + +int main(int argc, char const* argv[]) noexcept { + using namespace PNG_CICP_Editor; - return 0; + return ! PNG_CICP_Editor::parse_command_line_parameters(argc, argv) + .transform_error(print_monad_error) + .and_then(execute_action) + .has_value(); } \ No newline at end of file diff --git a/Code/png_cicp_editor/Error.hpp b/Code/png_cicp_editor/Error.hpp index c2ea4e3..6abc969 100644 --- a/Code/png_cicp_editor/Error.hpp +++ b/Code/png_cicp_editor/Error.hpp @@ -1,35 +1,42 @@ -// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef PNG_CICP_EDITOR_ERROR_HPP -#define PNG_CICP_EDITOR_ERROR_HPP - -#include -#include -#include - -namespace PNG_CICP_Editor { - - struct Error { - explicit Error(std::vector output_messages) noexcept; - - std::vector output_messages_; - }; - - void print_error(const Error& error) noexcept; - - template - struct ErrorWithCode : public Error { - explicit ErrorWithCode(T error_code, std::vector output_messages) noexcept - : Error{ std::move(output_messages) } - , error_code_{ std::move(error_code) } - {} - - T error_code_; - - }; - -} // namespace PNG_CICP_Editor - -#endif // #ifndef PNG_CICP_EDITOR_ERROR_HPP +// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PNG_CICP_EDITOR_ERROR_HPP +#define PNG_CICP_EDITOR_ERROR_HPP + +#include +#include +#include +#include + +namespace PNG_CICP_Editor { + + struct Error { + explicit Error(std::vector output_messages) noexcept; + + std::vector output_messages_; + }; + + void print_error(const Error& error) noexcept; + + template + T print_monad_error(const T& error) noexcept { + print_error(error); + return error; + } + + template + struct ErrorWithCode : public Error { + explicit ErrorWithCode(T error_code, std::vector output_messages) noexcept + : Error{ std::move(output_messages) } + , error_code_{ std::move(error_code) } + {} + + T error_code_; + + }; + +} // namespace PNG_CICP_Editor + +#endif // #ifndef PNG_CICP_EDITOR_ERROR_HPP diff --git a/Code/png_cicp_editor/FileReader.cpp b/Code/png_cicp_editor/FileReader.cpp index 3869d90..6f2c323 100644 --- a/Code/png_cicp_editor/FileReader.cpp +++ b/Code/png_cicp_editor/FileReader.cpp @@ -1,42 +1,42 @@ -// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "FileReader.hpp" - -#include -#include - -namespace { - - // error messages - static const std::string_view cannot_open_file = "Cannot open file: "; - static const std::string_view cannot_read_file = "Cannot read file: "; - - // utility - static const std::string_view newline = "\n"; - -} // anonymous namespace - -namespace PNG_CICP_Editor { - - std::expected, ReadFileError> read_file(const std::string& file_path) noexcept { - auto png_file = std::ifstream{ file_path.c_str(), std::ios::binary | std::ios::ate }; - if (!png_file.good()) { - return std::unexpected{ ReadFileError{ ReadFileErrorCode::CannotOpenFile, { cannot_open_file, file_path.c_str(), newline } } }; - } - auto file_size = png_file.tellg(); - png_file.seekg(0, std::ios::beg); - - auto buffer = std::vector( file_size ); - if (!png_file.read(buffer.data(), file_size)) - { - return std::unexpected{ ReadFileError{ ReadFileErrorCode::CannotReadFile, { cannot_read_file, file_path.c_str(), newline } } }; - } - - png_file.close(); - - return buffer; - } - -} // namespace PNG_CICP_Editor +// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "FileReader.hpp" + +#include +#include + +namespace { + + // error messages + static const std::string_view cannot_open_file = "Cannot open file: "; + static const std::string_view cannot_read_file = "Cannot read file: "; + + // utility + static const std::string_view newline = "\n"; + +} // anonymous namespace + +namespace PNG_CICP_Editor { + + std::expected, ReadFileError> read_file(const std::string& file_path) noexcept { + auto png_file = std::ifstream{ file_path.c_str(), std::ios::binary | std::ios::ate }; + if (!png_file.good()) { + return std::unexpected{ ReadFileError{ ReadFileErrorCode::CannotOpenFile, { cannot_open_file, file_path.c_str(), newline } } }; + } + auto file_size = png_file.tellg(); + png_file.seekg(0, std::ios::beg); + + auto buffer = std::vector( file_size ); + if (!png_file.read(buffer.data(), file_size)) + { + return std::unexpected{ ReadFileError{ ReadFileErrorCode::CannotReadFile, { cannot_read_file, file_path.c_str(), newline } } }; + } + + png_file.close(); + + return buffer; + } + +} // namespace PNG_CICP_Editor diff --git a/Code/png_cicp_editor/FileReader.hpp b/Code/png_cicp_editor/FileReader.hpp index 2cad373..b214d29 100644 --- a/Code/png_cicp_editor/FileReader.hpp +++ b/Code/png_cicp_editor/FileReader.hpp @@ -1,26 +1,26 @@ -// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef PNG_CICP_EDITOR_FILEREADER_HPP -#define PNG_CICP_EDITOR_FILEREADER_HPP - -#include -#include -#include - -#include "Error.hpp" - -namespace PNG_CICP_Editor { - - enum class ReadFileErrorCode { - CannotOpenFile, - CannotReadFile, - }; - using ReadFileError = ErrorWithCode; - - std::expected, ReadFileError> read_file(const std::string& file_path) noexcept; - -} // namespace PNG_CICP_Editor - -#endif // #ifndef PNG_CICP_EDITOR_FILEREADER_HPP +// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PNG_CICP_EDITOR_FILEREADER_HPP +#define PNG_CICP_EDITOR_FILEREADER_HPP + +#include +#include +#include + +#include "Error.hpp" + +namespace PNG_CICP_Editor { + + enum class ReadFileErrorCode { + CannotOpenFile, + CannotReadFile, + }; + using ReadFileError = ErrorWithCode; + + std::expected, ReadFileError> read_file(const std::string& file_path) noexcept; + +} // namespace PNG_CICP_Editor + +#endif // #ifndef PNG_CICP_EDITOR_FILEREADER_HPP diff --git a/Code/png_cicp_editor/FileWriter.cpp b/Code/png_cicp_editor/FileWriter.cpp index 0433c80..906c01f 100644 --- a/Code/png_cicp_editor/FileWriter.cpp +++ b/Code/png_cicp_editor/FileWriter.cpp @@ -1,43 +1,43 @@ -// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "FileWriter.hpp" - -#include -#include -#include - -namespace { - - // error messages - static const std::string_view cannot_open_file = "Cannot open file: "; - static const std::string_view cannot_write_file = "Cannot write file: "; - - // utility - static const std::string_view newline = "\n"; - -} // anonymous namespace - -namespace PNG_CICP_Editor { - - std::expected write_file(const std::string& file_path, std::vector> buffers) noexcept { - auto png_file = std::ofstream{ file_path.c_str(), std::ios::binary }; - if (!png_file.good()) { - return std::unexpected{ WriteFileError{ WriteFileErrorCode::CannotOpenFile, { cannot_open_file, file_path.c_str(), newline } } }; - } - - for (auto& buffer : buffers) { - png_file.write(buffer.data(), buffer.size_bytes()); - if (png_file.bad()) - { - return std::unexpected{ WriteFileError{ WriteFileErrorCode::CannotWriteFile, { cannot_write_file, file_path.c_str(), newline } } }; - } - } - - png_file.close(); - - return std::expected{}; - } - -} // namespace PNG_CICP_Editor +// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "FileWriter.hpp" + +#include +#include +#include + +namespace { + + // error messages + static const std::string_view cannot_open_file = "Cannot open file: "; + static const std::string_view cannot_write_file = "Cannot write file: "; + + // utility + static const std::string_view newline = "\n"; + +} // anonymous namespace + +namespace PNG_CICP_Editor { + + std::expected write_file(const std::string& file_path, std::vector> buffers) noexcept { + auto png_file = std::ofstream{ file_path.c_str(), std::ios::binary }; + if (!png_file.good()) { + return std::unexpected{ WriteFileError{ WriteFileErrorCode::CannotOpenFile, { cannot_open_file, file_path.c_str(), newline } } }; + } + + for (auto& buffer : buffers) { + png_file.write(buffer.data(), buffer.size_bytes()); + if (png_file.bad()) + { + return std::unexpected{ WriteFileError{ WriteFileErrorCode::CannotWriteFile, { cannot_write_file, file_path.c_str(), newline } } }; + } + } + + png_file.close(); + + return std::expected{}; + } + +} // namespace PNG_CICP_Editor diff --git a/Code/png_cicp_editor/FileWriter.hpp b/Code/png_cicp_editor/FileWriter.hpp index 3b1586d..29f832e 100644 --- a/Code/png_cicp_editor/FileWriter.hpp +++ b/Code/png_cicp_editor/FileWriter.hpp @@ -1,27 +1,27 @@ -// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef PNG_CICP_EDITOR_FILEWRITER_HPP -#define PNG_CICP_EDITOR_FILEWRITER_HPP - -#include -#include -#include -#include - -#include "Error.hpp" - -namespace PNG_CICP_Editor { - - enum class WriteFileErrorCode { - CannotOpenFile, - CannotWriteFile, - }; - using WriteFileError = ErrorWithCode; - - std::expected write_file(const std::string& file_path, std::vector> buffers) noexcept; - -} // namespace PNG_CICP_Editor - -#endif // #ifndef PNG_CICP_EDITOR_FILEWRITER_HPP +// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PNG_CICP_EDITOR_FILEWRITER_HPP +#define PNG_CICP_EDITOR_FILEWRITER_HPP + +#include +#include +#include +#include + +#include "Error.hpp" + +namespace PNG_CICP_Editor { + + enum class WriteFileErrorCode { + CannotOpenFile, + CannotWriteFile, + }; + using WriteFileError = ErrorWithCode; + + std::expected write_file(const std::string& file_path, std::vector> buffers) noexcept; + +} // namespace PNG_CICP_Editor + +#endif // #ifndef PNG_CICP_EDITOR_FILEWRITER_HPP diff --git a/Code/png_cicp_editor/PNGParser.cpp b/Code/png_cicp_editor/PNGParser.cpp index a355531..af6a1ad 100644 --- a/Code/png_cicp_editor/PNGParser.cpp +++ b/Code/png_cicp_editor/PNGParser.cpp @@ -1,57 +1,57 @@ -// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "PNGParser.hpp" - -#include -#include -#include - -namespace { - - // error messages - static const std::string_view not_a_png_file = "Not a valid PNG file."; - - // utility - static const std::string_view png_header = "\x89PNG\x0d\x0a\x1a\x0a"; - - constexpr uint32_t read_32_bits(const std::span& file_contents, size_t index) noexcept { - uint8_t first_byte = file_contents[index + 0]; - uint8_t second_byte = file_contents[index + 1]; - uint8_t third_byte = file_contents[index + 2]; - uint8_t fourth_byte = file_contents[index + 3]; - - // PNGs are little endian - // TODO: Make sure the target device is also little endian for this to work - uint32_t combined_bytes = first_byte << 24 | - second_byte << 16 | - third_byte << 8 | - fourth_byte << 0; - return combined_bytes; - } - -} // anonymous namespace - -namespace PNG_CICP_Editor { - - std::expected, GetChunkIndicesError> get_chunk_indices(const std::span& file_contents) noexcept { - if (png_header.compare(file_contents.data()) != 0) { - return std::unexpected{ GetChunkIndicesError{ GetChunkIndicesErrorCode::NotAPNGFile, { not_a_png_file } } }; - } - - std::vector chunk_indices; - size_t current_index = 8; // 8 comes from the the PNG header - while (current_index < file_contents.size()) { - chunk_indices.push_back(current_index); - - - uint32_t chunk_length = read_32_bits(file_contents, current_index); - // TODO: Check that chunk_length + 12 doesn't overflow - current_index += chunk_length + 12; // 12 comes from the chunk length, type, and crc data - } - - return chunk_indices; - } - -} // namespace PNG_CICP_Editor +// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "PNGParser.hpp" + +#include +#include +#include + +namespace { + + // error messages + static const std::string_view not_a_png_file = "Not a valid PNG file."; + + // utility + static const std::string_view png_header = "\x89PNG\x0d\x0a\x1a\x0a"; + + constexpr uint32_t read_32_bits(const std::span& file_contents, size_t index) noexcept { + uint8_t first_byte = file_contents[index + 0]; + uint8_t second_byte = file_contents[index + 1]; + uint8_t third_byte = file_contents[index + 2]; + uint8_t fourth_byte = file_contents[index + 3]; + + // PNGs are little endian + // TODO: Make sure the target device is also little endian for this to work + uint32_t combined_bytes = first_byte << 24 | + second_byte << 16 | + third_byte << 8 | + fourth_byte << 0; + return combined_bytes; + } + +} // anonymous namespace + +namespace PNG_CICP_Editor { + + std::expected, GetChunkIndicesError> get_chunk_indices(const std::span& file_contents) noexcept { + if (png_header.compare(file_contents.data()) != 0) { + return std::unexpected{ GetChunkIndicesError{ GetChunkIndicesErrorCode::NotAPNGFile, { not_a_png_file } } }; + } + + std::vector chunk_indices; + size_t current_index = 8; // 8 comes from the the PNG header + while (current_index < file_contents.size()) { + chunk_indices.push_back(current_index); + + + uint32_t chunk_length = read_32_bits(file_contents, current_index); + // TODO: Check that chunk_length + 12 doesn't overflow + current_index += chunk_length + 12; // 12 comes from the chunk length, type, and crc data + } + + return chunk_indices; + } + +} // namespace PNG_CICP_Editor diff --git a/Code/png_cicp_editor/PNGParser.hpp b/Code/png_cicp_editor/PNGParser.hpp index 42ddc74..220b60c 100644 --- a/Code/png_cicp_editor/PNGParser.hpp +++ b/Code/png_cicp_editor/PNGParser.hpp @@ -1,25 +1,25 @@ -// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef PNG_CICP_EDITOR_PNGPARSER_HPP -#define PNG_CICP_EDITOR_PNGPARSER_HPP - -#include -#include -#include - -#include "Error.hpp" - -namespace PNG_CICP_Editor { - - enum class GetChunkIndicesErrorCode { - NotAPNGFile, - }; - using GetChunkIndicesError = ErrorWithCode; - - std::expected, GetChunkIndicesError> get_chunk_indices(const std::span& file_contents) noexcept; - -} // namespace PNG_CICP_Editor - -#endif // #ifndef PNG_CICP_EDITOR_PNGPARSER_HPP +// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PNG_CICP_EDITOR_PNGPARSER_HPP +#define PNG_CICP_EDITOR_PNGPARSER_HPP + +#include +#include +#include + +#include "Error.hpp" + +namespace PNG_CICP_Editor { + + enum class GetChunkIndicesErrorCode { + NotAPNGFile, + }; + using GetChunkIndicesError = ErrorWithCode; + + std::expected, GetChunkIndicesError> get_chunk_indices(const std::span& file_contents) noexcept; + +} // namespace PNG_CICP_Editor + +#endif // #ifndef PNG_CICP_EDITOR_PNGPARSER_HPP diff --git a/Projects/VisualStudio/png_cicp_editor.vcxproj b/Projects/VisualStudio/png_cicp_editor.vcxproj index b298c78..facd85d 100644 --- a/Projects/VisualStudio/png_cicp_editor.vcxproj +++ b/Projects/VisualStudio/png_cicp_editor.vcxproj @@ -1,225 +1,225 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 17.0 - Win32Proj - {70a91e78-afc8-42e1-acbc-a09c78aad81f} - cicpinserter - 10.0 - - - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - - - Application - true - v143 - Unicode - - - Application - false - v143 - true - Unicode - - - - - - - - - - - - - - - - - - - - - $(SolutionDir)\$(MSBuildProjectName)\$(PlatformTarget)\$(Configuration)\ - $(SolutionDir)\$(MSBuildProjectName)\$(PlatformTarget)\$(Configuration)\ - $(ProjectDir)..\..\Dependencies\max\Code;$(IncludePath) - - - $(SolutionDir)\$(MSBuildProjectName)\$(PlatformTarget)\$(Configuration)\ - $(SolutionDir)\$(MSBuildProjectName)\$(PlatformTarget)\$(Configuration)\ - $(ProjectDir)..\..\Dependencies\max\Code;$(IncludePath) - - - $(SolutionDir)\$(MSBuildProjectName)\$(PlatformTarget)\$(Configuration)\ - $(SolutionDir)\$(MSBuildProjectName)\$(PlatformTarget)\$(Configuration)\ - $(ProjectDir)..\..\Dependencies\max\Code;$(IncludePath) - - - $(SolutionDir)\$(MSBuildProjectName)\$(PlatformTarget)\$(Configuration)\ - $(SolutionDir)\$(MSBuildProjectName)\$(PlatformTarget)\$(Configuration)\ - $(ProjectDir)..\..\Dependencies\max\Code;$(IncludePath) - - - - Level4 - true - WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpplatest - - - Console - true - - - - - - - - - Level4 - true - true - true - WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpplatest - - - Console - true - true - true - - - - - - - - - Level4 - true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpplatest - - - Console - true - - - - - - - - - Level4 - true - true - true - NDEBUG;_CONSOLE;%(PreprocessorDefinitions) - true - stdcpplatest - - - Console - true - true - true - - - - - - - - - + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 17.0 + Win32Proj + {70a91e78-afc8-42e1-acbc-a09c78aad81f} + cicpinserter + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)\$(MSBuildProjectName)\$(PlatformTarget)\$(Configuration)\ + $(SolutionDir)\$(MSBuildProjectName)\$(PlatformTarget)\$(Configuration)\ + $(ProjectDir)..\..\Dependencies\max\Code;$(IncludePath) + + + $(SolutionDir)\$(MSBuildProjectName)\$(PlatformTarget)\$(Configuration)\ + $(SolutionDir)\$(MSBuildProjectName)\$(PlatformTarget)\$(Configuration)\ + $(ProjectDir)..\..\Dependencies\max\Code;$(IncludePath) + + + $(SolutionDir)\$(MSBuildProjectName)\$(PlatformTarget)\$(Configuration)\ + $(SolutionDir)\$(MSBuildProjectName)\$(PlatformTarget)\$(Configuration)\ + $(ProjectDir)..\..\Dependencies\max\Code;$(IncludePath) + + + $(SolutionDir)\$(MSBuildProjectName)\$(PlatformTarget)\$(Configuration)\ + $(SolutionDir)\$(MSBuildProjectName)\$(PlatformTarget)\$(Configuration)\ + $(ProjectDir)..\..\Dependencies\max\Code;$(IncludePath) + + + + Level4 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpplatest + + + Console + true + + + + + + + + + Level4 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpplatest + + + Console + true + true + true + + + + + + + + + Level4 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpplatest + + + Console + true + + + + + + + + + Level4 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpplatest + + + Console + true + true + true + + + + + + + + + \ No newline at end of file diff --git a/Projects/VisualStudio/png_cicp_editor.vcxproj.filters b/Projects/VisualStudio/png_cicp_editor.vcxproj.filters index e1f94e1..dadd3a0 100644 --- a/Projects/VisualStudio/png_cicp_editor.vcxproj.filters +++ b/Projects/VisualStudio/png_cicp_editor.vcxproj.filters @@ -1,130 +1,130 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - Dependencies\max\Compiling - - - Dependencies\max\Compiling - - - Dependencies\max\Compiling\Configuration - - - Dependencies\max\Compiling\Configuration - - - Dependencies\max\Compiling\Configuration - - - Dependencies\max\Compiling\Configuration\Platform - - - Dependencies\max\Compiling\Configuration\Platform - - - Dependencies\max\Compiling\Configuration\Platform - - - Dependencies\max\Compiling\Configuration\Platform - - - Dependencies\max\Compiling\Configuration\Compiler - - - Dependencies\max\Compiling\Configuration\Compiler - - - Dependencies\max\Compiling\Configuration\Compiler - - - - - Dependencies\max\Compiling - - - - - - - - - - Dependencies\max - - - Dependencies\max - - - Docs - - - Docs - - - Docs - - - .github\ISSUE_TEMPLATE - - - .github\ISSUE_TEMPLATE - - - .github\workflows - - - - - {9491087b-ba77-42aa-b90e-b4f4bda5a415} - - - {ab47c8e2-c480-43d8-95ab-627d57d2016a} - - - {813b2f64-8891-48ee-a59d-946977fa04df} - - - {4a6257d8-62e5-4226-a464-b8731de52f6a} - - - {d518904b-b99f-4351-a11e-e5ffb072538e} - - - {c077ab64-27ce-490a-8b2d-fe11fbe50f61} - - - {564176d4-b0a5-446b-908c-e9070918704b} - - - {effbd925-3fe1-4e5e-9b05-4a2ca8aeb308} - - - {f5895d29-0eb2-43e6-904f-1c5b5ceb236f} - - - {98e1203a-09d1-452c-b423-b44d7189b0c2} - - + + + + + + + + + + + + + + + + + + + + + + + + + + Dependencies\max\Compiling + + + Dependencies\max\Compiling + + + Dependencies\max\Compiling\Configuration + + + Dependencies\max\Compiling\Configuration + + + Dependencies\max\Compiling\Configuration + + + Dependencies\max\Compiling\Configuration\Platform + + + Dependencies\max\Compiling\Configuration\Platform + + + Dependencies\max\Compiling\Configuration\Platform + + + Dependencies\max\Compiling\Configuration\Platform + + + Dependencies\max\Compiling\Configuration\Compiler + + + Dependencies\max\Compiling\Configuration\Compiler + + + Dependencies\max\Compiling\Configuration\Compiler + + + + + Dependencies\max\Compiling + + + + + + + + + + Dependencies\max + + + Dependencies\max + + + Docs + + + Docs + + + Docs + + + .github\ISSUE_TEMPLATE + + + .github\ISSUE_TEMPLATE + + + .github\workflows + + + + + {9491087b-ba77-42aa-b90e-b4f4bda5a415} + + + {ab47c8e2-c480-43d8-95ab-627d57d2016a} + + + {813b2f64-8891-48ee-a59d-946977fa04df} + + + {4a6257d8-62e5-4226-a464-b8731de52f6a} + + + {d518904b-b99f-4351-a11e-e5ffb072538e} + + + {c077ab64-27ce-490a-8b2d-fe11fbe50f61} + + + {564176d4-b0a5-446b-908c-e9070918704b} + + + {effbd925-3fe1-4e5e-9b05-4a2ca8aeb308} + + + {f5895d29-0eb2-43e6-904f-1c5b5ceb236f} + + + {98e1203a-09d1-452c-b423-b44d7189b0c2} + + \ No newline at end of file