AAPT2: Move format related files under same directory

Test: make aapt2_tests
Change-Id: Id72cdfc12ba3add294048e60c55f2461344464bf
diff --git a/tools/aapt2/format/Archive.cpp b/tools/aapt2/format/Archive.cpp
new file mode 100644
index 0000000..d152a9c
--- /dev/null
+++ b/tools/aapt2/format/Archive.cpp
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "format/Archive.h"
+
+#include <cstdio>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "android-base/errors.h"
+#include "android-base/macros.h"
+#include "android-base/utf8.h"
+#include "androidfw/StringPiece.h"
+#include "ziparchive/zip_writer.h"
+
+#include "util/Files.h"
+
+using ::android::StringPiece;
+using ::android::base::SystemErrorCodeToString;
+
+namespace aapt {
+
+namespace {
+
+class DirectoryWriter : public IArchiveWriter {
+ public:
+  DirectoryWriter() = default;
+
+  bool Open(const StringPiece& out_dir) {
+    dir_ = out_dir.to_string();
+    file::FileType type = file::GetFileType(dir_);
+    if (type == file::FileType::kNonexistant) {
+      error_ = "directory does not exist";
+      return false;
+    } else if (type != file::FileType::kDirectory) {
+      error_ = "not a directory";
+      return false;
+    }
+    return true;
+  }
+
+  bool StartEntry(const StringPiece& path, uint32_t flags) override {
+    if (file_) {
+      return false;
+    }
+
+    std::string full_path = dir_;
+    file::AppendPath(&full_path, path);
+    file::mkdirs(file::GetStem(full_path).to_string());
+
+    file_ = {::android::base::utf8::fopen(full_path.c_str(), "wb"), fclose};
+    if (!file_) {
+      error_ = SystemErrorCodeToString(errno);
+      return false;
+    }
+    return true;
+  }
+
+  bool Write(const void* data, int len) override {
+    if (!file_) {
+      return false;
+    }
+
+    if (fwrite(data, 1, len, file_.get()) != static_cast<size_t>(len)) {
+      error_ = SystemErrorCodeToString(errno);
+      file_.reset(nullptr);
+      return false;
+    }
+    return true;
+  }
+
+  bool FinishEntry() override {
+    if (!file_) {
+      return false;
+    }
+    file_.reset(nullptr);
+    return true;
+  }
+
+  bool WriteFile(const StringPiece& path, uint32_t flags, io::InputStream* in) override {
+    if (!StartEntry(path, flags)) {
+      return false;
+    }
+
+    const void* data = nullptr;
+    size_t len = 0;
+    while (in->Next(&data, &len)) {
+      if (!Write(data, static_cast<int>(len))) {
+        return false;
+      }
+    }
+    return !in->HadError();
+  }
+
+  bool HadError() const override {
+    return !error_.empty();
+  }
+
+  std::string GetError() const override {
+    return error_;
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DirectoryWriter);
+
+  std::string dir_;
+  std::unique_ptr<FILE, decltype(fclose)*> file_ = {nullptr, fclose};
+  std::string error_;
+};
+
+class ZipFileWriter : public IArchiveWriter {
+ public:
+  ZipFileWriter() = default;
+
+  bool Open(const StringPiece& path) {
+    file_ = {::android::base::utf8::fopen(path.to_string().c_str(), "w+b"), fclose};
+    if (!file_) {
+      error_ = SystemErrorCodeToString(errno);
+      return false;
+    }
+    writer_ = util::make_unique<ZipWriter>(file_.get());
+    return true;
+  }
+
+  bool StartEntry(const StringPiece& path, uint32_t flags) override {
+    if (!writer_) {
+      return false;
+    }
+
+    size_t zip_flags = 0;
+    if (flags & ArchiveEntry::kCompress) {
+      zip_flags |= ZipWriter::kCompress;
+    }
+
+    if (flags & ArchiveEntry::kAlign) {
+      zip_flags |= ZipWriter::kAlign32;
+    }
+
+    int32_t result = writer_->StartEntry(path.data(), zip_flags);
+    if (result != 0) {
+      error_ = ZipWriter::ErrorCodeString(result);
+      return false;
+    }
+    return true;
+  }
+
+  bool Write(const void* data, int len) override {
+    int32_t result = writer_->WriteBytes(data, len);
+    if (result != 0) {
+      error_ = ZipWriter::ErrorCodeString(result);
+      return false;
+    }
+    return true;
+  }
+
+  bool FinishEntry() override {
+    int32_t result = writer_->FinishEntry();
+    if (result != 0) {
+      error_ = ZipWriter::ErrorCodeString(result);
+      return false;
+    }
+    return true;
+  }
+
+  bool WriteFile(const StringPiece& path, uint32_t flags, io::InputStream* in) override {
+    while (true) {
+      if (!StartEntry(path, flags)) {
+        return false;
+      }
+
+      const void* data = nullptr;
+      size_t len = 0;
+      while (in->Next(&data, &len)) {
+        if (!Write(data, static_cast<int>(len))) {
+          return false;
+        }
+      }
+
+      if (in->HadError()) {
+        return false;
+      }
+
+      if (!FinishEntry()) {
+        return false;
+      }
+
+      // Check to see if the file was compressed enough. This is preserving behavior of AAPT.
+      if ((flags & ArchiveEntry::kCompress) != 0 && in->CanRewind()) {
+        ZipWriter::FileEntry last_entry;
+        int32_t result = writer_->GetLastEntry(&last_entry);
+        CHECK(result == 0);
+        if (last_entry.compressed_size + (last_entry.compressed_size / 10) >
+            last_entry.uncompressed_size) {
+          // The file was not compressed enough, rewind and store it uncompressed.
+          if (!in->Rewind()) {
+            // Well we tried, may as well keep what we had.
+            return true;
+          }
+
+          int32_t result = writer_->DiscardLastEntry();
+          if (result != 0) {
+            error_ = ZipWriter::ErrorCodeString(result);
+            return false;
+          }
+          flags &= ~ArchiveEntry::kCompress;
+
+          continue;
+        }
+      }
+      return true;
+    }
+  }
+
+  bool HadError() const override {
+    return !error_.empty();
+  }
+
+  std::string GetError() const override {
+    return error_;
+  }
+
+  virtual ~ZipFileWriter() {
+    if (writer_) {
+      writer_->Finish();
+    }
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ZipFileWriter);
+
+  std::unique_ptr<FILE, decltype(fclose)*> file_ = {nullptr, fclose};
+  std::unique_ptr<ZipWriter> writer_;
+  std::string error_;
+};
+
+}  // namespace
+
+std::unique_ptr<IArchiveWriter> CreateDirectoryArchiveWriter(IDiagnostics* diag,
+                                                             const StringPiece& path) {
+  std::unique_ptr<DirectoryWriter> writer = util::make_unique<DirectoryWriter>();
+  if (!writer->Open(path)) {
+    diag->Error(DiagMessage(path) << writer->GetError());
+    return {};
+  }
+  return std::move(writer);
+}
+
+std::unique_ptr<IArchiveWriter> CreateZipFileArchiveWriter(IDiagnostics* diag,
+                                                           const StringPiece& path) {
+  std::unique_ptr<ZipFileWriter> writer = util::make_unique<ZipFileWriter>();
+  if (!writer->Open(path)) {
+    diag->Error(DiagMessage(path) << writer->GetError());
+    return {};
+  }
+  return std::move(writer);
+}
+
+}  // namespace aapt