Echo Writes Code

main.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#include "crucible/boba/errors.hpp"
#include "crucible/boba/metadata.hpp"
#include "crucible/os/errors.hpp"
#include "crucible/os/fs.hpp"

#include <cstdlib>
#include <iostream>
#include <optional>
#include <string>
#include <variant>
#include <vector>

namespace crucible::bobadump {

struct NotAFile final {
  [[nodiscard]]
  auto describe() const -> std::string {
    return "Not a regular file";
  }
};

class BobaDumpError final {
public:
  explicit BobaDumpError(const boba::errors::BobaError &variant) :
    my_variant { variant } {
  }

  explicit BobaDumpError(const os::errors::OsError &variant) :
    my_variant { variant } {
  }

  explicit BobaDumpError(const NotAFile &variant) :
    my_variant { variant } {
  }

  [[nodiscard]]
  auto describe() const -> std::string {
    auto const describer {
      [](auto &&variant) {
        return variant.describe();
      }
    };

    return std::visit(describer, my_variant);
  }

private:
  std::variant<boba::errors::BobaError, os::errors::OsError, NotAFile> my_variant;
};

template<typename T>
using BobaDumpResult = core::either::Either<BobaDumpError, T>;

[[nodiscard]]
auto read_boba_container(std::string const &boba_file_path) -> BobaDumpResult<boba::metadata::Container> {
  auto const is_file_result = os::fs::is_file(boba_file_path);

  if (is_file_result.has_lhs()) {
    return core::either::make_lhs<BobaDumpError>(is_file_result.get_lhs());
  }

  bool const is_file = is_file_result.get_rhs();

  if (!is_file) {
    return core::either::make_lhs<BobaDumpError>(NotAFile {});
  }

  auto read_file_data_result = os::fs::read_file_data(boba_file_path);

  if (read_file_data_result.has_lhs()) {
    return core::either::make_lhs<BobaDumpError>(read_file_data_result.get_lhs());
  }

  auto const file_contents = std::move(read_file_data_result).get_rhs();
  auto from_bytes_result = crucible::boba::metadata::from_bytes(file_contents);

  if (from_bytes_result.has_lhs()) {
    return core::either::make_lhs<BobaDumpError>(from_bytes_result.get_lhs());
  }

  return from_bytes_result.forward_rhs();
}

auto dump_boba_container(crucible::boba::metadata::Container const &metadata) -> void {
  auto const schema = metadata.get_schema();

  std::cout
    << "• Schema block:\n"
    << "  • Full header count: " << schema.get_full_header_count() << "\n"
    << "  • User header count: " << schema.get_user_header_count() << "\n"
    << "  • Size of each header: " << schema.get_header_size() << " bytes\n"
    << "  • Schema entry count: " << schema.get_entry_count() << "\n"
    << "  • Size of each schema entry: " << schema.get_entry_size() << " bytes\n";

  std::size_t index = 0;
  for (auto const &header : metadata) {
    std::cout
      << "• User block " << index << ":\n"
      << "  • Signature: 0x" << std::hex << header.signature << std::dec << "\n"
      << "  • Data size: " << header.data_size << " bytes\n"
      << "  • User data[0]: 0x" << std::hex << header.user_data[0] << std::dec << "\n"
      << "  • User data[1]: 0x" << std::hex << header.user_data[1] << std::dec << "\n"
      << "  • Data starts at offset: " << header.offset << "\n";

    ++index;
  }
}

}

auto main(int argc, char *argv[]) -> int {
  std::vector<std::string> const arguments(argv + 1, argv + argc);
  std::optional<std::string> boba_file_path {};

  auto current = arguments.begin();
  auto const end = arguments.end();

  while (current != end) {
    if (!boba_file_path.has_value()) {
      boba_file_path = *current++;
      continue;
    }

    std::cerr << "Unrecognized option or argument '" << *current << "'\n";
    return EXIT_FAILURE;
  }

  if (!boba_file_path.has_value()) {
    std::cerr << "Missing argument BOBA_FILE\n";
    return EXIT_FAILURE;
  }

  auto const container = crucible::bobadump::read_boba_container(boba_file_path.value());
  if (container.has_lhs()) {
    std::cerr << argv[0] << ": " << container.get_lhs().describe() << "\n";
    return EXIT_FAILURE;
  }

  crucible::bobadump::dump_boba_container(container.get_rhs());
  return EXIT_SUCCESS;
}