Support reading multinode logs raw

The header is in a separate file, so teach log_cat --raw to read that.

Change-Id: Iba5e7c66e91da078f1525c67c537104813cdcc14
diff --git a/aos/events/logging/log_cat.cc b/aos/events/logging/log_cat.cc
index c8407f1..2e23e0a 100644
--- a/aos/events/logging/log_cat.cc
+++ b/aos/events/logging/log_cat.cc
@@ -24,6 +24,8 @@
             "log file");
 DEFINE_bool(raw, false,
             "If true, just print the data out unsorted and unparsed");
+DEFINE_string(raw_header, "",
+              "If set, the file to read the header from in raw mode");
 DEFINE_bool(format_raw, true,
             "If true and --raw is specified, print out raw data, but use the "
             "schema to format the data.");
@@ -81,7 +83,17 @@
       LOG(FATAL) << "Expected 1 logfile as an argument.";
     }
     aos::logger::MessageReader reader(argv[1]);
-    std::cout << aos::FlatbufferToJson(reader.log_file_header(),
+
+    std::optional<aos::logger::MessageReader> raw_header_reader;
+    const aos::logger::LogFileHeader *full_header = reader.log_file_header();
+    if (!FLAGS_raw_header.empty()) {
+      raw_header_reader.emplace(FLAGS_raw_header);
+      CHECK_EQ(
+          reader.log_file_header()->configuration_sha256()->string_view(),
+          aos::logger::Sha256(raw_header_reader->raw_log_file_header().span()));
+      full_header = raw_header_reader->log_file_header();
+    }
+    std::cout << aos::FlatbufferToJson(full_header,
                                        {.multi_line = FLAGS_pretty,
                                         .max_vector_size = static_cast<size_t>(
                                             FLAGS_max_vector_size)})
@@ -94,9 +106,10 @@
       if (!message) {
         break;
       }
-      const aos::Channel *channel =
-          reader.log_file_header()->configuration()->channels()->Get(
-              message.value().message().channel_index());
+      const auto *const channels = full_header->configuration()->channels();
+      const size_t channel_index = message.value().message().channel_index();
+      CHECK_LT(channel_index, channels->size());
+      const aos::Channel *const channel = channels->Get(channel_index);
 
       if (FLAGS_format_raw && message.value().message().data() != nullptr) {
         std::cout << aos::configuration::StrippedChannelToString(channel) << " "
diff --git a/aos/events/logging/logfile_sorting.cc b/aos/events/logging/logfile_sorting.cc
index b1a993e..9e406e9 100644
--- a/aos/events/logging/logfile_sorting.cc
+++ b/aos/events/logging/logfile_sorting.cc
@@ -5,14 +5,14 @@
 #include <string>
 #include <utility>
 #include <vector>
-#include "dirent.h"
-#include "sys/stat.h"
 
 #include "aos/events/logging/logfile_utils.h"
 #include "aos/flatbuffer_merge.h"
 #include "aos/flatbuffers.h"
 #include "aos/time/time.h"
+#include "dirent.h"
 #include "openssl/sha.h"
+#include "sys/stat.h"
 
 namespace aos {
 namespace logger {
@@ -20,20 +20,6 @@
 
 namespace {
 
-std::string Sha256(const absl::Span<const uint8_t> str) {
-  unsigned char hash[SHA256_DIGEST_LENGTH];
-  SHA256_CTX sha256;
-  SHA256_Init(&sha256);
-  SHA256_Update(&sha256, str.data(), str.size());
-  SHA256_Final(hash, &sha256);
-  std::stringstream ss;
-  for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
-    ss << std::hex << std::setw(2) << std::setfill('0')
-       << static_cast<int>(hash[i]);
-  }
-  return ss.str();
-}
-
 // Check if string ends with ending
 bool EndsWith(std::string_view str, std::string_view ending) {
   return str.size() >= ending.size() &&
@@ -626,5 +612,19 @@
   return stream;
 }
 
+std::string Sha256(const absl::Span<const uint8_t> str) {
+  unsigned char hash[SHA256_DIGEST_LENGTH];
+  SHA256_CTX sha256;
+  SHA256_Init(&sha256);
+  SHA256_Update(&sha256, str.data(), str.size());
+  SHA256_Final(hash, &sha256);
+  std::stringstream ss;
+  for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
+    ss << std::hex << std::setw(2) << std::setfill('0')
+       << static_cast<int>(hash[i]);
+  }
+  return ss.str();
+}
+
 }  // namespace logger
 }  // namespace aos
diff --git a/aos/events/logging/logfile_sorting.h b/aos/events/logging/logfile_sorting.h
index 65bb1de..f479d62 100644
--- a/aos/events/logging/logfile_sorting.h
+++ b/aos/events/logging/logfile_sorting.h
@@ -96,6 +96,9 @@
 // Recursively searches for logfiles in argv[1] and onward.
 std::vector<std::string> FindLogs(int argc, char **argv);
 
+// Returns the sha256 of a span.
+std::string Sha256(const absl::Span<const uint8_t> str);
+
 }  // namespace logger
 }  // namespace aos
 
diff --git a/aos/events/logging/logger.cc b/aos/events/logging/logger.cc
index e10d911..acbbff2 100644
--- a/aos/events/logging/logger.cc
+++ b/aos/events/logging/logger.cc
@@ -5,6 +5,7 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/uio.h>
+
 #include <vector>
 
 #include "absl/strings/escaping.h"
@@ -39,18 +40,6 @@
 namespace aos {
 namespace logger {
 namespace {
-std::string Sha256(const absl::Span<const uint8_t> str) {
-  unsigned char hash[SHA256_DIGEST_LENGTH];
-  SHA256_CTX sha256;
-  SHA256_Init(&sha256);
-  SHA256_Update(&sha256, str.data(), str.size());
-  SHA256_Final(hash, &sha256);
-  std::stringstream ss;
-  for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
-    ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
-  }
-  return ss.str();
-}
 
 std::string LogFileVectorToString(std::vector<LogFile> log_files) {
   std::stringstream ss;